nested hstore patch

Started by Teodor Sigaevabout 12 years ago53 messages
#1Teodor Sigaev
teodor@sigaev.ru
1 attachment(s)

Hi!

Attatched patch adds nesting feature, types (string, boll and numeric values),
arrays and scalar to hstore type.

All new features are described in PGConf.EU talk
http://www.sai.msu.su/~megera/postgres/talks/hstore-dublin-2013.pdf (since PGCon
some features was added).

Patch includes:
1 implementaion SRF_RETURN_NEXT_NULL()
2 contrib/hstore changes
3 docs of new hstore module (many thanks to David E. Wheeler
<david.wheeler@pgexperts.com>)

In current state patch is in WIP status, for short period I plan to move support
of binary nested structure to core to share binary representation for hstore and
json types.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

Attachments:

nested_hstore-0.40.patch.gzapplication/x-tar; name=nested_hstore-0.40.patch.gzDownload
#2Andrew Dunstan
andrew@dunslane.net
In reply to: Teodor Sigaev (#1)
Re: nested hstore patch

On 11/12/2013 01:35 PM, Teodor Sigaev wrote:

Hi!

Attatched patch adds nesting feature, types (string, boll and numeric
values), arrays and scalar to hstore type.

All new features are described in PGConf.EU talk
http://www.sai.msu.su/~megera/postgres/talks/hstore-dublin-2013.pdf
(since PGCon some features was added).

Patch includes:
1 implementaion SRF_RETURN_NEXT_NULL()
2 contrib/hstore changes
3 docs of new hstore module (many thanks to David E. Wheeler
<david.wheeler@pgexperts.com>)

In current state patch is in WIP status, for short period I plan to
move support of binary nested structure to core to share binary
representation for hstore and json types.

Thanks, Teodor.

As soon as we have that shared binary representation available, I will
be working on adapting it to JSON.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Peter Eisentraut
peter_e@gmx.net
In reply to: Teodor Sigaev (#1)
Re: nested hstore patch

On 11/12/13, 1:35 PM, Teodor Sigaev wrote:

Attatched patch adds nesting feature, types (string, boll and numeric
values), arrays and scalar to hstore type.

Could you check your email client for next time? It's sending
Content-Type: application/x-tar for a *.patch.gz file.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Hannu Krosing
hannu@2ndQuadrant.com
In reply to: Andrew Dunstan (#2)
Re: nested hstore patch

On 11/13/2013 01:37 AM, Andrew Dunstan wrote:

On 11/12/2013 01:35 PM, Teodor Sigaev wrote:

Hi!

Attatched patch adds nesting feature, types (string, boll and numeric
values), arrays and scalar to hstore type.

All new features are described in PGConf.EU talk
http://www.sai.msu.su/~megera/postgres/talks/hstore-dublin-2013.pdf
(since PGCon some features was added).

Patch includes:
1 implementaion SRF_RETURN_NEXT_NULL()
2 contrib/hstore changes
3 docs of new hstore module (many thanks to David E. Wheeler
<david.wheeler@pgexperts.com>)

In current state patch is in WIP status, for short period I plan to
move support of binary nested structure to core to share binary
representation for hstore and json types.

Thanks, Teodor.

As soon as we have that shared binary representation available, I will
be working on adapting it to JSON.

As I remember from earlier discussions, current json has some
artefacts that some people want to preserve and which are incompatible
with hstore approach where you have actual object behind the serialisation.

I remember strong voices in support of *not* normalising json, so that
things like

{"a":1,"a":true, "a":"b", "a":none}

would go through the system unaltered, for claimed standard usage of
json as
"processing instructions". That is as source code which can possibly
converted
to JavaScript Object and not something that would come out of
serialising of
any existing JavaScript Object.

I suggest we add another type, maybe jsobj, which has input and output
as standard
"JSON" but which is defined from the start to be equivalent of existing
object
and not "preservable source code" to such object.

Cheers

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic OÜ

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5David E. Wheeler
david@justatheory.com
In reply to: Hannu Krosing (#4)
Re: nested hstore patch

On Nov 13, 2013, at 3:59 PM, Hannu Krosing <hannu@2ndquadrant.com> wrote:

I remember strong voices in support of *not* normalising json, so that
things like

{"a":1,"a":true, "a":"b", "a":none}

would go through the system unaltered, for claimed standard usage of
json as
"processing instructions". That is as source code which can possibly
converted
to JavaScript Object and not something that would come out of
serialising of
any existing JavaScript Object.

My recollection from PGCon was that there was consensus to normalize on the way in -- or at least, if we switched to a binary representation as proposed by Oleg & Teodor, it was not worth the hassle to try to keep it.

I suggest we add another type, maybe jsobj, which has input and output
as standard
"JSON" but which is defined from the start to be equivalent of existing
object
and not "preservable source code" to such object.

-1 Let's try to keep this simple. See also VARCHAR and VARCHAR2 on Oracle.

Best,

David

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Hannu Krosing
hannu@2ndQuadrant.com
In reply to: David E. Wheeler (#5)
Re: nested hstore patch

On 11/14/2013 01:32 AM, David E. Wheeler wrote:

On Nov 13, 2013, at 3:59 PM, Hannu Krosing <hannu@2ndquadrant.com> wrote:

I remember strong voices in support of *not* normalising json, so that
things like

{"a":1,"a":true, "a":"b", "a":none}

would go through the system unaltered, for claimed standard usage of
json as
"processing instructions". That is as source code which can possibly
converted
to JavaScript Object and not something that would come out of
serialising of
any existing JavaScript Object.

My recollection from PGCon was that there was consensus to normalize on
the way in --

Great news! I remember advocating this approach in the mailing lists
but having been out-voted based on "current real-world usage out there" :)

or at least, if we switched to a binary representation as proposed by
Oleg & Teodor, it was not worth the hassle to try to keep it.

Very much agree. For the source code approach I'd recommend
text type with maybe a check that it is possible to convert it to json.

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic O�

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Andrew Dunstan
andrew@dunslane.net
In reply to: Hannu Krosing (#6)
Re: nested hstore patch

On 11/14/2013 03:21 AM, Hannu Krosing wrote:

On 11/14/2013 01:32 AM, David E. Wheeler wrote:

On Nov 13, 2013, at 3:59 PM, Hannu Krosing <hannu@2ndquadrant.com> wrote:

I remember strong voices in support of *not* normalising json, so that
things like

{"a":1,"a":true, "a":"b", "a":none}

would go through the system unaltered, for claimed standard usage of
json as
"processing instructions". That is as source code which can possibly
converted
to JavaScript Object and not something that would come out of
serialising of
any existing JavaScript Object.

My recollection from PGCon was that there was consensus to normalize on
the way in --

Great news! I remember advocating this approach in the mailing lists
but having been out-voted based on "current real-world usage out there" :)

or at least, if we switched to a binary representation as proposed by
Oleg & Teodor, it was not worth the hassle to try to keep it.

Very much agree. For the source code approach I'd recommend
text type with maybe a check that it is possible to convert it to json.

I don't think you and David are saying the same thing. AIUI he wants one
JSON type and is prepared to discard text preservation (duplicate keys
and key order). You want two json types, one of which would feature text
preservation.

Correct me if I'm wrong.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Hannu Krosing
hannu@2ndQuadrant.com
In reply to: Andrew Dunstan (#7)
Re: nested hstore patch

On 11/14/2013 01:47 PM, Andrew Dunstan wrote:

On 11/14/2013 03:21 AM, Hannu Krosing wrote:

On 11/14/2013 01:32 AM, David E. Wheeler wrote:

On Nov 13, 2013, at 3:59 PM, Hannu Krosing <hannu@2ndquadrant.com>
wrote:

I remember strong voices in support of *not* normalising json, so that
things like

{"a":1,"a":true, "a":"b", "a":none}

would go through the system unaltered, for claimed standard usage of
json as
"processing instructions". That is as source code which can possibly
converted
to JavaScript Object and not something that would come out of
serialising of
any existing JavaScript Object.

My recollection from PGCon was that there was consensus to normalize on
the way in --

Great news! I remember advocating this approach in the mailing lists
but having been out-voted based on "current real-world usage out
there" :)

or at least, if we switched to a binary representation as proposed by
Oleg & Teodor, it was not worth the hassle to try to keep it.

Very much agree. For the source code approach I'd recommend
text type with maybe a check that it is possible to convert it to json.

I don't think you and David are saying the same thing. AIUI he wants
one JSON
type and is prepared to discard text preservation (duplicate keys and
key order).
You want two json types, one of which would feature text preservation.

I actually *want* the same thing that David wants, but I think that
Merlin has
valid concerns about backwards compatibility.

If we have let this behaviour in, it is not nice to break several uses
of it now.

If we could somehow turn "old json" into a text domain with json syntax
check
(which it really is up to 9.3) via pg_upgrade that would be great.

It would be the required for pg_dump to have some swicth to output
different typename in CREATE TABLE and similar.

Correct me if I'm wrong.

cheers

andrew

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic O�

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Andres Freund
andres@2ndquadrant.com
In reply to: Teodor Sigaev (#1)
Re: nested hstore patch

Hi,

On 2013-11-12 22:35:31 +0400, Teodor Sigaev wrote:

Attatched patch adds nesting feature, types (string, boll and numeric
values), arrays and scalar to hstore type.

I took a quick peek at this:
* You cannot simply catch and ignore errors by doing
+	PG_TRY();
+	{
+		n = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(s->val), 0, -1));
+	}
+	PG_CATCH();
+	{
+		n = NULL;
+	}
+	PG_END_TRY();
 That skips cleanup and might ignore some errors (think memory allocation
 failures). But why do you even want to silently ignore errors there?
* Shouldn't the checks for v->size be done before filling the
  datastructures in makeHStoreValueArray() and makeHStoreValuePairs()?
* could you make ORDER_PAIRS() a function instead of a macro? It's
  pretty long and there's no reason not to use a function.
* You call numeric_recv via recvHStoreValue via recvHStore without
  checks on the input length. That seems - without having checked it in
  detail - a good way to read unrelated memory. Generally ISTM the input
  needs to be more carefully checked in the whole recv function.
* There's quite some new new, completely uncommented, code. Especially
  in hstore_op.c.
* the _PG_init you added should probably do a EmitWarningsOnPlaceholders().
* why does hstore need it's own atoi?
* shouldn't all the function prototypes be marked as externs?
* Lots of trailing whitespaces, quite some long lines, cuddly braces,
  ...
* I think hstore_compat.c's header should be updated.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Bruce Momjian
bruce@momjian.us
In reply to: Hannu Krosing (#8)
Re: nested hstore patch

On Thu, Nov 14, 2013 at 05:50:02PM +0100, Hannu Krosing wrote:

If we could somehow turn "old json" into a text domain with json syntax
check
(which it really is up to 9.3) via pg_upgrade that would be great.

It would be the required for pg_dump to have some swicth to output
different typename in CREATE TABLE and similar.

I don't think pg_upgrade isn't in a position to handle this. I think it
would require a script to be run after pg_upgrade completes.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Robert Haas
robertmhaas@gmail.com
In reply to: Hannu Krosing (#4)
Re: nested hstore patch

On Wed, Nov 13, 2013 at 6:59 PM, Hannu Krosing <hannu@2ndquadrant.com> wrote:

I remember strong voices in support of *not* normalising json, so that
things like

{"a":1,"a":true, "a":"b", "a":none}

would go through the system unaltered, for claimed standard usage of
json as
"processing instructions". That is as source code which can possibly
converted
to JavaScript Object and not something that would come out of
serialising of
any existing JavaScript Object.

Yeah, as the guy who wrote the original version of the JSON type,
which works just exactly like the XML type does, I stronly object to
changing the behavior. And doubly so now that it's released, as we
would be breaking backward compatibility.

I suggest we add another type, maybe jsobj, which has input and output
as standard
"JSON" but which is defined from the start to be equivalent of existing
object
and not "preservable source code" to such object.

I think this was the consensus solution when this was last discussed,
and I support it. There is similar space for a binary XML data type
if someone feels like implementing it. I think the names that were
proposed previously were something like jsonb and xmlb.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Bruce Momjian
bruce@momjian.us
In reply to: Robert Haas (#11)
Re: nested hstore patch

On Tue, Nov 19, 2013 at 10:51:06AM -0500, Robert Haas wrote:

I think this was the consensus solution when this was last discussed,
and I support it. There is similar space for a binary XML data type
if someone feels like implementing it. I think the names that were
proposed previously were something like jsonb and xmlb.

The natural name is OBJSON, meaning object JSON, because as PostgreSQL
people, we have to double-use letters wherever possible. ;-)

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ Everyone has their own god. +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Andrew Dunstan
andrew@dunslane.net
In reply to: Robert Haas (#11)
Re: nested hstore patch

On 11/19/2013 10:51 AM, Robert Haas wrote:

I suggest we add another type, maybe jsobj, which has input and output
as standard
"JSON" but which is defined from the start to be equivalent of existing
object
and not "preservable source code" to such object.

I think this was the consensus solution when this was last discussed,
and I support it. There is similar space for a binary XML data type
if someone feels like implementing it. I think the names that were
proposed previously were something like jsonb and xmlb.

I think that's the consensus position on a strategy.

JSONB seems to be the current winner min the name stakes.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Robert Haas
robertmhaas@gmail.com
In reply to: Bruce Momjian (#12)
Re: nested hstore patch

On Tue, Nov 19, 2013 at 10:55 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Tue, Nov 19, 2013 at 10:51:06AM -0500, Robert Haas wrote:

I think this was the consensus solution when this was last discussed,
and I support it. There is similar space for a binary XML data type
if someone feels like implementing it. I think the names that were
proposed previously were something like jsonb and xmlb.

The natural name is OBJSON, meaning object JSON, because as PostgreSQL
people, we have to double-use letters wherever possible. ;-)

Personally, I think the patch author should just run ps auxww | md5 |
sed 's/^[^0-9]//' | cut -c1-8 and call the new data type by the
resulting name.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Andrew Dunstan
andrew@dunslane.net
In reply to: Robert Haas (#14)
Re: nested hstore patch

On 11/19/2013 11:00 AM, Robert Haas wrote:

On Tue, Nov 19, 2013 at 10:55 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Tue, Nov 19, 2013 at 10:51:06AM -0500, Robert Haas wrote:

I think this was the consensus solution when this was last discussed,
and I support it. There is similar space for a binary XML data type
if someone feels like implementing it. I think the names that were
proposed previously were something like jsonb and xmlb.

The natural name is OBJSON, meaning object JSON, because as PostgreSQL
people, we have to double-use letters wherever possible. ;-)

Personally, I think the patch author should just run ps auxww | md5 |
sed 's/^[^0-9]//' | cut -c1-8 and call the new data type by the
resulting name.

My personal vote goes for "marmaduke". I've been dying to call some
builtin object that for ages.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Peter Eisentraut
peter_e@gmx.net
In reply to: Teodor Sigaev (#1)
Re: nested hstore patch

On 11/12/13, 1:35 PM, Teodor Sigaev wrote:

Hi!

Attatched patch adds nesting feature, types (string, boll and numeric
values), arrays and scalar to hstore type.

Documentation doesn't build:

openjade:hstore.sgml:206:16:E: document type does not allow element "VARLISTENTRY" here; assuming missing "VARIABLELIST" start-tag

Compiler warnings:

hstore_io.c: In function 'array_to_hstore':
hstore_io.c:1736:29: error: 'result' may be used uninitialized in this function [-Werror=maybe-uninitialized]

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17David E. Wheeler
david@justatheory.com
In reply to: Peter Eisentraut (#16)
Re: nested hstore patch

On Nov 20, 2013, at 6:19 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

openjade:hstore.sgml:206:16:E: document type does not allow element "VARLISTENTRY" here; assuming missing "VARIABLELIST" start-tag

Thanks, I fixed this one.

David

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18David E. Wheeler
david@justatheory.com
In reply to: Teodor Sigaev (#1)
Re: nested hstore patch

On Nov 12, 2013, at 10:35 AM, Teodor Sigaev <teodor@sigaev.ru> wrote:

Hi!

Attatched patch adds nesting feature, types (string, boll and numeric values), arrays and scalar to hstore type.

My apologies for not getting to this sooner, work has been a bit nutty. The truth is that I reviewed this patch quite a bit a month back, mostly so I could write documentation, the results of which are included in this patch. And I'm super excited for what's to come in the next iteration, as I hear that Teodor and Andrew are hard at work adding jsonb as a binary-compatible JSON data type.

Meanwhile, for this version, a quick overview of what has changed since 9.2.

Contents & Purpose
==================

Improved Data Type Support
--------------------------

* Added data type support for values. Previously they could only be strings or NULL, but with this patch they can also be numbers or booleans.

* Added array support. Values can be arrays of other values. The format for arrays is a bracketed, comma-delimited list.

* Added nesting support. hstore values can themselves be hstores. Nested hstores are wrapped in braces, but the root-level hstore is not (for compatibility with the format of previous versions of hstore).

* An hstore value is no longer required to be an hstore object. It can now be any scalar value.

These three items make the basic format feature-complete with JSON. Here's an example where the values are scalars:

=% SELECT 'foo'::hstore, '"hi \"bob\""'::hstore, '1.0'::hstore, 'true'::hstore, NULL::hstore;
hstore | hstore | hstore | hstore | hstore
--------+--------------+--------+--------+--------
"foo" | "hi \"bob\"" | 1.0 | t |

And here are a couple of arrays with strings, numbers, booleans, and NULLs:

SELECT '[k,v]'::hstore, '[1.0, "hi there", false, null]'::hstore;
hstore | hstore
------------+----------------------------
["k", "v"] | [1.0, "hi there", f, NULL]

Here's a complicated example formatted with `hstore.pretty_print` enabled.

=% SET hstore.pretty_print=true;
=% SELECT '{
"type" => "Feature",
"bbox" => [-180.0, -90.0, 180.0, 90.0],
"geometry" => {
"type" => "Polygon",
"coordinates" => [[
[-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 +
] +
] +
] +
}

So, exact feature parity with the JSON data type.

* hstore.pretty_print is a new GUC, specifically to allow an HSTORE value to be pretty-printed. There is also a function to pretty-print, so we might be able to just do away with the GUC.

Interface
---------

* New operators:
  + `hstore -> int`:     Get string value at array index (starting at 0)
  + `hstore ^> text`:    Get numeric value for key
  + `hstore ^> int`:     Get numeric value at array index
  + `hstore ?> text`:    Get boolean value for key
  + `hstore ?> int`:     Get boolean value at array index
  + `hstore #> text[]`:  Get string value for key path
  + `hstore #^> text[]`: Get numeric value for key path
  + `hstore #?> text[]`: Get boolean value for key path
  + `hstore %> text`:    Get hstore value for key
  + `hstore %> int`:     Get hstore value at array index
  + `hstore #%> text[]`: Get hstore value for key path
  + `hstore ? int`:      Does hstore contain array index
  + `hstore #? text[]`:  Does hstore contain key path
  + `hstore - int`:      Delete index from left operand
  + `hstore #- text[]`:  Delete key path from left operand
* New functions:
  + `hstore(text)`:             Make a text scalar hstore
  + `hstore(numeric)`:          Make a numeric scalar hstore
  + `hstore(boolean)`:          Make a boolean scalar hstore
  + `hstore(text, hstore)`:     Make a nested hstore
  + `hstore(text, numeric)`:    Make an hstore with a key and numeric value
  + `hstore(text, boolean)`:    Make an hstore with a key and boolean value
  + `array_to_hstore(anyarray): Make an array hstore from an SQL array
  + `hvals(hstore)`             Get values as a set of hstore values
  + `json_to_hstore(json)`      Convert JSON to hstore
  + `each_hstore(hstore)`       Get set of hstore key/value pairs
  + `hstore_typeof(hstore)`     Return text name for the hstore type (hash, array, text, numeric, etc.)
  + `replace(hstore,text[],hstore)`:     Replace value at specified path
  + `concat_path(hstore,text[],hstore)`: Concatenate hstore value at specified path
  + `hstore_print(hstore, params)`:      Format hstore as text

The hstore_print() function has a number of optional boolean parameters to affect how the resulting text is formatted. They all default to false:

- pretty_print
- array_curly_braces: use {} instead of [] for arrays
- root_hash_decorated: Use {} for the root hash
- json: Format as JSON
- loose: Try to parse numbers and booleans from text values

Other Changes
-------------

* New casts: JSON and HSTORE can be cast to each other. I don't think they're implicit, though the forthcoming jsonb data type might support explicit casting to and from hstore, since internally they will be identical.

* The internal representation has been changed, but should be backward (and pg_upgrade) compatible, just as Andrew Gierth's change from 8.4 to 9.0 was. One can do an in-place update to rewrite all records at once. Of course, nested and/or non-hash hstore values dumped from 9.4 will not be able to be loaded into 9.3.

* GIN indexing is now supported. This is actually pretty amazing. For an hstore value, even hash keys are considered values, as far as the index is concerned. This makes it efficient to find hstore values that contain a key. I wrote an example in this blog post:

http://theory.so/pg/2013/10/25/indexing-nested-hstore/

Submission review
=================
* Is the patch in a patch format which has context? Yes.
* Does it apply cleanly to the current git master? It did for me, though I think Peter has found an issue or two since.
* Does it include reasonable tests, necessary doc patches, etc? Yes.

Usability review
================

* Does the patch actually implement what it says it does? Yes.
* Do we want that? OH yes.
* Do we already have it? No.
* Does it follow SQL spec, or the community-agreed behavior? Yes, though want jsonb, too.
* Does it include pg_dump support? Yes
* Are there dangers? Could break backward compatibility, though I don't think it does.
* Have all the bases been covered? I think so

Feature test
============

* Does the feature work as advertised? Yes.
* Are there corner cases the author has failed to consider? All I noticed were promptly fixed.
* Are there any assertion failures or crashes? No

Performance review
==================

* Does the patch slow down simple tests? No
* If it claims to improve performance, does it? Yes, with GIN index support. Loading hstore values is slower than loading JSON, but everything else is faster than JSON.
* Does it slow down other things? No.

Coding review
=============

* Does it follow the project guidelines? Yes.
* Are there portability issues? Unknown
* Will it work on Windows/BSD etc? Tested on OS X only.
* Are the comments sufficient and accurate? Yes.
* Does it do what it says, correctly? As best I can tell, yes.
* Does it produce compiler warnings? No.
* Can you make it crash? No, but I did find a bug or two that was promptly fixed.

Architecture review
===================

* Is everything done in a way that fits together coherently with other features/modules? yes.
* Are there interdependencies that can cause problems? No

Conclusion
==========

I love where nested hstore is going, especially since it will be used for jsonb, too. The nesting, data type, and GIN index support is really great, and the new constructors provide a nice SQL API that make it easy to use. I think that the next version of this patch will be full of win for the project.

This was considered a WIP patch, since the jsonb support is still forthcoming, so it's appropriate to leave it marked “Returned with feedback”. As Andrew is doing much of that work, the code itself will get a much closer examination from him. But for the hstore feature itself, I think the current interface and features are ready to go.

Best,

David

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Andres Freund
andres@2ndquadrant.com
In reply to: David E. Wheeler (#18)
Re: nested hstore patch

On 2013-12-20 15:16:30 -0800, David E. Wheeler wrote:

But for the hstore feature itself, I think the current interface and features are ready to go.

I think this patch needs significant amount of work because it can be
considered ready for committer. I found the list of issues in
http://archives.postgresql.org/message-id/20131118163633.GE20305%40awork2.anarazel.de
within 10 minutes, indicating that it clearly cannot be ready yet.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Robert Haas
robertmhaas@gmail.com
In reply to: David E. Wheeler (#18)
Re: nested hstore patch

On Fri, Dec 20, 2013 at 6:16 PM, David E. Wheeler <david@justatheory.com> wrote:

* New operators:
+ `hstore -> int`:     Get string value at array index (starting at 0)
+ `hstore ^> text`:    Get numeric value for key
+ `hstore ^> int`:     Get numeric value at array index
+ `hstore ?> text`:    Get boolean value for key
+ `hstore ?> int`:     Get boolean value at array index
+ `hstore #> text[]`:  Get string value for key path
+ `hstore #^> text[]`: Get numeric value for key path
+ `hstore #?> text[]`: Get boolean value for key path
+ `hstore %> text`:    Get hstore value for key
+ `hstore %> int`:     Get hstore value at array index
+ `hstore #%> text[]`: Get hstore value for key path
+ `hstore ? int`:      Does hstore contain array index
+ `hstore #? text[]`:  Does hstore contain key path
+ `hstore - int`:      Delete index from left operand
+ `hstore #- text[]`:  Delete key path from left operand

Although in some ways there's a certain elegance to this, it also
sorta looks like punctuation soup. I can't help wondering whether
we'd be better off sticking to function names.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Hannu Krosing
hannu@2ndQuadrant.com
In reply to: Robert Haas (#20)
Re: nested hstore patch

On 12/23/2013 12:28 PM, Robert Haas wrote:

On Fri, Dec 20, 2013 at 6:16 PM, David E. Wheeler <david@justatheory.com> wrote:

* New operators:
+ `hstore -> int`:     Get string value at array index (starting at 0)
+ `hstore ^> text`:    Get numeric value for key
+ `hstore ^> int`:     Get numeric value at array index
+ `hstore ?> text`:    Get boolean value for key
+ `hstore ?> int`:     Get boolean value at array index
+ `hstore #> text[]`:  Get string value for key path
+ `hstore #^> text[]`: Get numeric value for key path
+ `hstore #?> text[]`: Get boolean value for key path
+ `hstore %> text`:    Get hstore value for key
+ `hstore %> int`:     Get hstore value at array index
+ `hstore #%> text[]`: Get hstore value for key path
+ `hstore ? int`:      Does hstore contain array index
+ `hstore #? text[]`:  Does hstore contain key path
+ `hstore - int`:      Delete index from left operand
+ `hstore #- text[]`:  Delete key path from left operand

Although in some ways there's a certain elegance to this, it also
sorta looks like punctuation soup. I can't help wondering whether
we'd be better off sticking to function names.

Has anybody looked into how hard it would be to add "method" notation
to postgreSQL, so that instead of calling

getString(hstorevalue, n)

we could use

hstorevalue.getString(n)

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic O�

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hannu Krosing (#21)
Re: nested hstore patch

Hello

2013/12/23 Hannu Krosing <hannu@2ndquadrant.com>

On 12/23/2013 12:28 PM, Robert Haas wrote:

On Fri, Dec 20, 2013 at 6:16 PM, David E. Wheeler <david@justatheory.com>

wrote:

* New operators:
+ `hstore -> int`:     Get string value at array index (starting at 0)
+ `hstore ^> text`:    Get numeric value for key
+ `hstore ^> int`:     Get numeric value at array index
+ `hstore ?> text`:    Get boolean value for key
+ `hstore ?> int`:     Get boolean value at array index
+ `hstore #> text[]`:  Get string value for key path
+ `hstore #^> text[]`: Get numeric value for key path
+ `hstore #?> text[]`: Get boolean value for key path
+ `hstore %> text`:    Get hstore value for key
+ `hstore %> int`:     Get hstore value at array index
+ `hstore #%> text[]`: Get hstore value for key path
+ `hstore ? int`:      Does hstore contain array index
+ `hstore #? text[]`:  Does hstore contain key path
+ `hstore - int`:      Delete index from left operand
+ `hstore #- text[]`:  Delete key path from left operand

Although in some ways there's a certain elegance to this, it also
sorta looks like punctuation soup. I can't help wondering whether
we'd be better off sticking to function names.

Has anybody looked into how hard it would be to add "method" notation
to postgreSQL, so that instead of calling

getString(hstorevalue, n)

we could use

hstorevalue.getString(n)

yes, I played with it some years ago. I ended early, there was a problem
with parser - when I tried append a new rule. And because there was not
simple solution, I didn't continue.

But it can be nice feature - minimally for plpgsql coders.

Regards

Pavel

Show quoted text

--
Hannu Krosing
PostgreSQL Consultant
Performance, Scalability and High Availability
2ndQuadrant Nordic OÜ

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Jonathan S. Katz
jonathan.katz@excoventures.com
In reply to: Robert Haas (#20)
Re: nested hstore patch

On Dec 23, 2013, at 6:28 AM, Robert Haas wrote:

On Fri, Dec 20, 2013 at 6:16 PM, David E. Wheeler <david@justatheory.com> wrote:

* New operators:
+ `hstore -> int`:     Get string value at array index (starting at 0)
+ `hstore ^> text`:    Get numeric value for key
+ `hstore ^> int`:     Get numeric value at array index
+ `hstore ?> text`:    Get boolean value for key
+ `hstore ?> int`:     Get boolean value at array index
+ `hstore #> text[]`:  Get string value for key path
+ `hstore #^> text[]`: Get numeric value for key path
+ `hstore #?> text[]`: Get boolean value for key path
+ `hstore %> text`:    Get hstore value for key
+ `hstore %> int`:     Get hstore value at array index
+ `hstore #%> text[]`: Get hstore value for key path
+ `hstore ? int`:      Does hstore contain array index
+ `hstore #? text[]`:  Does hstore contain key path
+ `hstore - int`:      Delete index from left operand
+ `hstore #- text[]`:  Delete key path from left operand

Although in some ways there's a certain elegance to this, it also
sorta looks like punctuation soup. I can't help wondering whether
we'd be better off sticking to function names.

The key thing is making it easy for people to easily chain calls to their nested hstore objects, and I think these operators accomplish that.

Some of them are fairly intuitive, and I think as a community if we have a) good docs, b) good blog posts on how to use nested hstore, and c) provides clear instructions @ PG events on how to use it, it would be okay, though some things, i.e. extracting the key by a path, might be better being in a function anyway. However, having it as an operator might encourage more usage, only because people tend to think that "functions will slow my query down."

My only concern is the consistency with the generally accepted standard of JSON and with the upcoming jsonb type. I'm not sure if the jsonb API has been defined yet, but it would be great to keep consistency between nested hstore and jsonb so people don't have to learn two different access systems. Data extraction from JSON is often done by the dot operator in implementations, and depending on the language you are in, there are ways to add / test existence / remove objects from the JSON blob.

Being able to extract objects from nested hstore / JSON using the dot operator would be simple and intuitive and general well-understood, but of course there are challenges with doing that in PG and well, proper SQL.

Jonathan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Oleg Bartunov
obartunov@gmail.com
In reply to: Andres Freund (#9)
1 attachment(s)
Re: nested hstore patch

Attached is a new version of patch, which addresses most issues raised
by Andres.

It's long holidays in Russia now and it happened that Teodor is
traveling with family, so Teodor asked me to reply. Comments in code
will be added asap.

Oleg

On Mon, Nov 18, 2013 at 8:36 PM, Andres Freund <andres@2ndquadrant.com> wrote:

Hi,

On 2013-11-12 22:35:31 +0400, Teodor Sigaev wrote:

Attatched patch adds nesting feature, types (string, boll and numeric
values), arrays and scalar to hstore type.

I took a quick peek at this:
* You cannot simply catch and ignore errors by doing
+       PG_TRY();
+       {
+               n = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(s->val), 0, -1));
+       }
+       PG_CATCH();
+       {
+               n = NULL;
+       }
+       PG_END_TRY();
That skips cleanup and might ignore some errors (think memory allocation
failures). But why do you even want to silently ignore errors there?

Fixed: now it's ignore only ERRCODE_INVALID_TEXT_REPRESENTATION error.
Our stringIsNumber() could be too optimistic.

* Shouldn't the checks for v->size be done before filling the
datastructures in makeHStoreValueArray() and makeHStoreValuePairs()?

Why check before filling? The result will be the same (throwing an
error) but it saves a loop over array/hash. String values aren't
copied during parse process.

* could you make ORDER_PAIRS() a function instead of a macro? It's
pretty long and there's no reason not to use a function.

fixed - macro and delaction argument was used for development.

* You call numeric_recv via recvHStoreValue via recvHStore without
checks on the input length. That seems - without having checked it in
detail - a good way to read unrelated memory. Generally ISTM the input
needs to be more carefully checked in the whole recv function.

numeric_recv checks this, otherwise we can say that numeric_recv
could not check its input. numeric_recv() gets a StringInfo buffer.

* There's quite some new new, completely uncommented, code. Especially
in hstore_op.c.

We'll comment asap

* the _PG_init you added should probably do a EmitWarningsOnPlaceholders().

fixed

* why does hstore need it's own atoi?

because:
* our pg_atoi should work with non-C-string (not finished with \0)
* doesn't throw an exception on error

* shouldn't all the function prototypes be marked as externs?

fixed

* Lots of trailing whitespaces, quite some long lines, cuddly braces,

tried to fix

...
* I think hstore_compat.c's header should be updated.

yes, we'll do with

Show quoted text

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Attachments:

nested_hstore-0.42.patch.gzapplication/x-gzip; name=nested_hstore-0.42.patch.gzDownload
����R��{��(�7�)P�8"�%������V�:�_II����o�k��IJ�*����`��+I���1�]`07�``O\���g�3Z��j11�/W����O'g3����w;���|b}�2�������aX������i��Y�wZ�V&�~`m]�����?������C�X8���j�_W����c��� N��E���>~/-c���1�;�d*��{#h�u����5�o�c3�v���r�-�[#�5N�����__���3��x�����{��<��;s���2�~6Y���f�k~qi��?v�5kau��|�U3�/�����/�0�s����W�/^�
�i?;89���ZG�,?LC�5�C���CD�{q5�4����cS!|��J��M&m� ��0�I(���N������q���V�	�9��c������i���IG�O_ �n:+��X���p����������'��������7��y���<?}���O/���3����qR��tQ���j~yj^M��=Y@�N�a��f���v��i��7.�������4���P��zJ,>��t���sf���uz�1��z�~|q4�<x~�Z��mct�r�\>b����d����E��:��pyq��nV��[���w+������:�d@.��2a���F�k
������������\?�]M�$YM��k������po���XJuxE��������/������=������b����w��x"�ny�|Hj�9y������E����9�u|*�<9�,Wx;���:�l�d�s��z����W��v9����P:^�a��bb��Nk��9����VS��Z���8�Bx�n�-��WS� ���fKX@.����C/^/&`�)���7����Y��=7���
�[����jb����|��8�����S��i�T����������6j��a���o������f�}��O�������PZ�jI�eq���@�-����������(��'�P�cD���>����y!�W�I]����B[�&B/����w@�-�o��PkU��Cu�U���8�3�t���������k����TCno�R���k��}��z���5���:���12zTC���Xs������h$j��A��M�����2m�14G����j��=�x
�6�����0U]u�=�k��Zo��G=��i�#j�a��w`���s�=�g��~_�]�;�[CQ�;���>�g����"���@w�C�j��5v,Q��j}�G�t�����o���]{���:Rm��kt�������cM���@�i�g���P����v��M��L��l]�G}��3��a����7���%|{���G��m��5��>�2-w�SE
��2�������l[�@�-��0m�k����>��]� �}���v�=kd����f��:������=8O5f���������?&��#�Z\Qc���hL���j4R�o�@>�$SSMg��D
w����?�z�F|�4��6H��`0����ke'|U�d�}#[�U��s\�����C��F��������?c�t��A�9#sd���G�������;����ul�FD����x���1t�
���`������5&������T�?"W��@P{���.(
i�>�U�'���R��Cp5S���������Q
������1���=��?����O|��c���k������m���?Z�6t�G|Yvwh�$�����lSh}��k`?��?F��TuH�A�5��#�G��]��#|��=�m��}��
�>`��������1X3���8��%9�`8��>�� C�?z=���C���U���$g�P���'�l[���%�G_U�C�Kp����4��3TV#�Yc�f���Ff_'��:pG}��3����S�o�l�7����c����A2�1�
���G:���Ccd�������t���b���i��.��RMc`��N��
�5���V
CF�gj�>��1�~o�%|�����G��]��H������qo���Y����}Cc`�
C���:���`���;&9����N�
a���k�nGpGCud@�"�tT
��m0V�����z�'�#u�� E5��;���T���c�4sH�������m{�1���a������
6�j8��!Tcqz$�Cu������fk������#K����k������Ls0t	_c`Z�g���k�U��������6�W:b���n�sw�v{�K��4�2�hP�"|����I�]m���*�1�Mg(��e4�;"�&8���F������A|��\�K��Tk8\�T��P���L���$���*T�E���&�
��cA���t����1��o�]���X��]� ��}[E|�a��Q����&��pd_�t]]'������>U��������c��������pD���|�}��#���qhh$��	��4�>U0T��m}��	_�o�AI�C���$�T�6mW�C2������������K|�\�u��4�����������p����9�
�����7����0�
�o��:�N���8�}�?F��?�NA5F*�`t3]�&�k�uYnO�a_{���q�?=G���}��;���D�0���`@����j�BP��`������|h��a8�60z��
�T��I�`N�&
����uMW�c�9}��vLH�soh��;$���]��C��Z�iX�����c�H+{f������B%��
���^���P'��]{<������N���R�&��o@�*�_7�n�E���S��cu��u�[�P�k�H�����;��q��,�Y�N��)�����X�=�
����r�o&_8k�Ok`2��F#�����c\$���w���E�V�:3D�fj���p����|�����f�]�;��1�h��V����"���Kj��W��<��w�(���(���(\���H/�Dq._��[�8��o&����?'f�?�Y��a���l�2�X��������r3�<5M�Fj��b2�Y���i9a+p��
�����
1�w�������|�t,hH����VIJ
	�����z�N���0���h���� IfO�����K�(k�v
c�I����i}��#^��cu��{/vXCc�����i���C�Z�B-|����� �R}��AlE!B{�!B�t��v}����?����?F���@��������`>4�� ��"���|�0��e��vo��������X�
��������`���']9@�b�a���?v��?([7�	z]au�u���H�����.�u��#A{���tF�P&m�*����Q��BY��,����(�a��xg �CFB�I�R	��M�������O���|zu1��bW�����W�t����t�|�s;m���G�lq�S�l�vc	����q����]��O�
��Qa�j��suE�X����3��
;�a,�M��=8::��-HG�uv���
jI�����Z`o������N����^��\���B�-�� R�#Ru���CP�������	�������Z�m�}�Oopk����G�h/����dx���U���$zj�����u��TY�5�Lo���Zj ��r.�a_�� �q?��as�x!����xo{���8m�L�
GB�X4��)J�����2��\Z�"K�Mr
+O��L� G����b�%���2W�_��
M� ������������[��9��,zir/1)�n�E T�]"$��i����������@�1�5�i��d���skKs����	���%�"f��U_U�]P��?��(!��A��9U&"�?f��hJ�����?qwc&8UJe%��YXZ���u�_�3����t6��7��&i�-��IN��{a�c��i�&���BA�%������[.�	�*)�R��l"�	�O����R�.c
�R����aY���"vC6������(d�;&f�@{��dE�c���6�pE������4����g��h��4��������d%Vv�j�����C�+�@��0��>{2�M@Ao�$�$%����X�)1/�����TZ��4V���6�����p�Z"n.��w����O��/�����s�d�b~�����Yq0qu�s@R���G����X��Mv�0S�;!7����J9�U:@��.���m�K��Ga������ ��(�5�Z8���9YDL��
~h�Xbt�s�,�l��84�����t�!�����;�
 `�����i�ax�@�.��f�g1�f3.2�9!�lNfKg����@��
?��V��\�"P�*��W�����7��R������UR��%�J�������7����a"�,QK��r;u���>���oe��z[��U��`+1+��
���1m���$��0���8f��f��^B���s�=��=a�/�L&�H��%vp�o��_/���::<><a��.�x�J���%�A����c����|��@=��8��r�?w����a�S8�bJ[l��
�����f��sG������#���~ CN^�;$��L�(��=Y�&3����|�;�M�\��!����>[-�0�4B���<7������c����q�K��s9sz{(F�w0��)~a9���{	�kW�d������:�d�"��}�?_�?��h��:nOK��6|}������@�^�zt��Jw����v�(n���
��#���o�A�;���Zez&~����t~�)���*J�O��Gc������,��
�N�$L���O��k���+�.�4"�H;r����-����y������n�``o���Z�����O%i������{�wU�p�yG���g�U��S��K�f�E�)\���T�������{�H0�xH\�>
�R��.�D���3�����p�l,R�K���eQ5��OZ�'8$�"4C��)�|��x��I���\5|t�j��)2����_9M���<���J����+�5�MO�����b�U�LAlI$��Fd!G�bVc��[1k-�v��Y������ �%�T�����`��q�Vv<�Sz�
]���6�,�����AN4������Tj�M=���u�!7-��C��!�����w�F����������Q�u����G[b�N:��bA�z�u�k���~[�@g������iDpxWb�����for��J��M�'b!�V��]c�td�nK������p^��AGKF��9�f0A�txwuZ��4�u������,G������kR����qSW�����&;Q('O���!(�3s>�%�9-:c8�t#J�$����j�1�EKSt���p;�����OO�.���-�Y����
�bY�_|3(/"�u��On�I]����"V
���{�r|�H��X���YW��ap��0��,�>�mv�0[�����zkk�O0]���'�����Kay����D������N�-0����;��	f����u�GC�T�����;�6m�:}GM*��/�pk��������m���h��i��*���t������kYr��n�rRn�������������}�qK�U��b�����#��I�3�$m�T"L�����az��op\��!�G��^���-V��������+o7��H�vgl~���1)4�������t���5
C���m�Dp�]��1����q��������k�Dvk&�u�>q��A�E�)@o�%p��P��b
�Jj��F%�FDq���I�g��� J���e0lm�f���D-L6nJ��x����L���(`�B�d7d�G)<(`��q���.gX�6K$q�m4{`��d��@KSb��_�O"�,L��`��(:��I��Q+	>��%�x9�MN7���oX�m��d	�W��b�N�����zRs%�b��CE/��PSI��X�*
F�*�k��C�2<6�w�;��Z�^]�&��q+c4��}2��>'w�����w��b�;o�A,9�{$�7���u�m��O�1o����P���#�9��.�)��SSp��w���}��w�/�Z��>����~�m��4ed����?�>fBu#'�>??�U��L�Z������,Xf`�� ����t��a�o�T)�^���nP��/xn$������!;9���!m��L�1�0�����0:�4�_^\V�������������o/=<��3���dW�j�{G_��h��� �r~y55V�)?1���r��9o����_�6�cv%���
]��A.(��RX��:`Mh��2y������P)��/M�b-b���J0|q�L���M�@�q��v��8�9�V�r7�p�
���3������'/M�oGL�BK����r���N��M���n�-���k���b���6�%m��4���0��e*c)H����(���gwq��H.(�N<sM�t��	��TAyf��,d�<�����,0Nb��?���f�l�V|��aDE�v��Yr��44��5�o���H�7��2e��("0��]���+��_9�
����=Z�I����KQ��)�������kE
7_bTD�[I��.�v�_��l2;�������>(�YE��*���1��ft���7IX]9
%T���P��Cd��Lf!z+��"a18��uN?Q���s�(���Y\(��"�uJ���'~A�����K>����G�'C���&L1������jnq��>�%4��.O��N+�H'��M�_���n�m������vs+ZOG/���3���	�@-�@A5�=�8CK�/Z��h�~�����a.���e)Q��"����m]���x ������r�P�|u����=�����C�M~q3�;e��u�`����wu��D��C��	���\Tc��_s�� �p�.��b��Q�����8��GN�C��;���=�><������0a���P�b	
����V��1�-#�u3�n��>>��gf�{�]�P��Lp�4p�����L5���m�f�:�T����f�f��/��7�\�i�y8x���#����D��*�{����)o�Ynn����}+h�rPp/��~{����:\.�}?�(��6�P)Mj���'A{3/�{�%���0���n,������5[��1����d������f�Vf�|-f���m
9��i,���{�6
��es>c���Ak�EAg��C��y��Q��������w��u���_��i_����
�����PD��54dy�����\�����O�5S=�fE�������������g+��g+���V��E�X��X���X%��i+����S��L�0�����]k����35q��l1SQ�������5������ngv�j��������Dyg���l�B�*U4_�L����-���}r��}/p��
.a���C�[
r�F6H��r`Z1���17���AWX�F4l����Z���Y�MI$�6gM�"�v�b���BU��s�PU��
�}�zA��]��������\;���"=Y]|S�v�K1n���i�I��S����S�9��P+a[3��`]0�i3�<�����dbl��>���}�+�`[f������m����E��:���}���ll���f��~��9����5������������j/s>���2�:[h/s���2����e��*�`�
Z���"����+y +�C�
_K��`�`��Wg�A�}��U�m4x�f0{!���WQ�\�t-��]����{WE�������{��[#�X�(���a�[�	�o�-4�&x}�����}{��io�=������e����_l�Y��������%������(���2��%����64�)g����W
,g	�(��VE�"�K������-��%�*N�!a�H�H6�S��b^cf���S�U�*4f����5/n���n�������/��_����uOhD�Q�9��iEe�rT��*[kI(y�&E���#��Yqx]�y��x'�:+���=�m!�\�0�r��E�V�C�8���.���w����������nh�u5��&��]�_�).���o�F����:������C|6�sVl�MF6��Y�{<W��a����fT�D�����[�g�_���~q���i�g�]���v`�J�G�d�1����?�+��|}��"m�]�l��t~q��m_X�2M�`�������Vh��E�v>���y����1��5���t_<�j��H�To��$��1��3�6I���W0��h��l��X8i�3��[�i��Q�����<To�+`m��������,���� ���	�{������@�ev�[)��on�0��^6C����^�l�q���H���n�u�-�|��p.�F(E���XcT���fw����\'�+�����Ee�\�g�6�GS�����,��
r3��>�60��OmE���?1Ss�����2�*��k!�~�+,"|���,c����<�4V����X	 f����j:-�����OI�em��PzLp��Uai)�n��b�m���\����_z�Q~�z��J(����nU�V�g�i�u0�9������Nm��Bm�y&W�B�+�����t��W�L[n>�������H����_����'��Fc	Oz���)qT�wEu�_7��u��%�������^l~Q���c��3(�X������m������n��v���������kPT_����M��;r������X���^=b�4lqQN4������d>�i=;<9x��,o��'�8_(�j�|�t��c��������Q����]6_�]e�+��l6w�dvy�Z��E>L��}����7,f������}~� M0���o�x}�c�<�6���D�OvW��r�_N/�/����gy�uE<��'������9	~(-��h,���C�f�W4.���!����m}y.�g)�~��<��I��5I�H����hG�4O1�$��y�C�L�g|��C/,�-��~����y����������;7v��������&���v��hp+]��	�o�2��>���B�g~�E>�ys�-�����������t�R;"Ox�0����ECW��7�~8�����uK~5�F8>�H&���
Fe�����p4�8������{#�[��?�ZG>lSA�=������G@o;��P��������T��\����3��B��iiXPV�Ak��c���h���
���8���=3������Vn�~f������UU��gfZ���weg�U���3����T���8q���+��*w�{&����43]�O�33]�53-83]�SZr����wt@��5~?s��p\N�^�V�����%�Fk#m�3 
������3�5�J��FGz7��v���5���f��V74�L�]�y�cX��s�#��A�U��l��wn�g\9^C��h�3J�Y��T<��H{o
 SC�w�[d���3�oV�M���T����0J�W����q�k������D�ySe������y�f����b�)�m����!=S;��7�$t����}���S�S��'��`j=�����
�t��9�8�v�G�xT������.t<�?W��(#~B���2[aN��!ufeQM�O�d��nm�b;���[~��v�(1$�J\}��������.�I������Y�nN/��������y\���J��u��`T����Lag�O��N��	�Al�DB���1���>�.MT�I��B(rV��0Q"tx�y]�o���(v���nY��%H��l$=�+v��.F����p�c���0���D�$�n=%���,�6��-+��H1GV�]Z?,�N����p1�e���G�%BXDx���%r.#A����+��#�.����R��%�cx���K4����c�?*����B�������0�b����Oi[�I�W8.@��b��z��|�5������W��B�������L�J����U]V*�~�"�C,�p�?�����K6�9�M����q;�r8Y�����s�-����po1��N�����p<�(/K����F�nD5���6�](i��ZW�����0,gY���.������a�%����n�hg�#B���]�����=��G�X�0kK������1]:Ex}O�7����pY�o������R�{5!����Z�3��,w��������!��ze���fpu���=��k���]]�����h���Z9��
�$�����e���d����|�������Y����Kg��_���_fg�|d�d�����0MU���df;����:�����}}hbm��!.���Z�B-��k���Z�2��������;�#_@x�6���h���W^��
�m
T�d"���U���GZ��&�E"xI9N�����-eT
i��^-���fA��W�X7��A�#d�h��&NR�
��(�W��)rQn&��	���+����?���<s�1��Q�TL_i���4\kW��AZ��#�����l�ZB�ZG������:��8jq��MI��A��v�H�}��N����m����
�YL�������v�+�d��)�NBR����:�2) j5S�7�]kc���ik����h����+���<�����hm�h��g5Z6�L���[Xj����A�����hm�z�!�O�8�v&�RX�
I'��iF�3RH�������%i�Y�����=�'���ds���IVGm���v�I���l��[�v�Ui�[�J�#��T�W�����������ez���;��0�X�'_���kq'P�PBSCU��g�7|{`�[��m�5o����(��;�*�}-��A���<�c?I��������Y��[BC��>_�r�k�����m0�_��X%������
/���?ef�LY`ZyZ���$ZW�@Vb`&��,�W������
�}�U	��
3Q�Z_�Z��t_���W��2�,��������r����j_IW���^�[�+)0s��Q����.?��_�?���I�.�!�mHE���E_
�'E�q�6�N���8��ga�	���VV`�B	�_�`x`@.(\o H��g1�8(����i�+����?����P�u�4v?}�����2mf.CA����<������v�^��<����`>z$��i��Xlw��Q��i��Yh���V3-��W�W�L���/��K���
Q��g��7���9�7H�J�W��	���_��_��"�"_�(|�O�:
����
n���I�
/xD���uzt[L�*^�|�h�SJ|"^[��������)�;�V�*]�O�p&�����OP�R�P�����M,ou=�O��-�Q���h�ua�N����%��K���\e�S��	{�I�Tv�$�V4�t/��b�p�����\�A��5�����2�q]�pZ���<�^�kca���q�\����)=�>���O���+}x!19��t��1�2P���W��~O�:����Eb]z��.�2��,��U6���GW�Jm*��L"�Fl��YA�����::���,?L�A��R������T�H����P�sW����X��A����rjLfl�����\��d��������_Bq�-����`�sc��'{ztxpr��yr�����W;�?�|�~��~����(x����o�a���&h�n�����B�������d���~������:+��
����*t��?����q0��v����~}yx�����W��*�b6���zy������S�L���_~��������C��~sxtp����;�����N���}A���^<�Y<D������O��zt�������3)���1f)��E)�+P�����fS�Q
V(D(B.,�
���8���+���F�+��[��J��n����;���fQ\Ru��WM{1
y"�)�A����'�,��H���Mq3�N�:�8B{I�&����7�
r���s&�cZ�n�����<LKQ5[���m��,�U���Z�#���2���q�Rg��'�C�#���(����	|zp|��)�[��;��8�91)���i�\-���i����Sq�����6��{5J�E;�4MZ:S|�_�x%��k>���4��s^:�@�Q^��{k>4�T�����L���/�]�j�U�E�:/B-gS�� $����
�&�j��4�����bc����R�	X�O�,p`	�Y:��[�c'8U;�y�D��@�l9���Te@�*�M���8��h�J)���B����
������N`z|x��'Psk���q�b!r "� ���r�u+�� v�^rYgq1���G���Q�n��|%�y�f�c/q���N�Y�A�?�<��]�*J�����,nK���"��&~U��o�2�(�0���2��?������/V�x��i,��/���\b)���������V��I�
W����*��"�{$'RN�����)H�~�r7T���}H�m_���2�����g��\G�ez�����0
�q~	2�	^������9~��9��qD�`������	�7>��&>�Rh]\6�R��)+�%q.�n20�l0UF�L/���D-�Q����*[|������v&������v�����h*�k�
������$��r��1l�mpm�S��"�Pd����~}y"2�%%)�2����eV����<�Z�H�-�fE>'���n���;���i��nO�C��������Vi�?��}M�w�����?��������=�65��=�vhO?�R�b�&>�"*�����q����W��i��l[����V5������1�-��7������]����Nf��c����Kc����/^��:x��������>jm���_O��#�o�L�#/A$��G�O_�v�F6���z�D��/�0���9����nf���Y,'���=1���C��99�)EF���C8�)�F��P��h�C[���N������)^N'��,����������G�m�F\��R�"��T�:{�D�g,�D������N6�~����m7�!TIs�^D[����E�H^l��!U���P�0�su��$3��@�*��V1f��>3��M0c��w2s���B��!�h��}�X��L!������a�kVr�IA!�M�A��@���d��]T���M��
�9xbCh=yx�J����e�S���M�
&z�T�
ttF�i�W;�4k�P���,���c��b�_=}M-!�}����k�>~���	�;��O*!�n����c�y$a������0�?[��:���ae`"��{�UF���^{�������;@�
�,�"����HnKBn���[>vk�����X4�cy����6c���(jY+0��U�����y4��e	�F��T��� C,��CQ��8�y�G5u�u�j)*��G�.pd(��^�dQ�E�5�[<-�l�x���Vs�����ZL>U'���B�7�P��M#�@3T�&�[��3"�h%��Ah��\�����,�U�u)��x���h�X`Y[,��b��G<�,6HG��r�P��
X^�/�����	w�a�u��VI�>x�g�bP+�<�w�~��~�?�_�G�������q�����|���I��}���3K_v(�������{�>�.��Y�	v��u)?[�����O��|�.��5(������m��
��~h��L+
����udn�<u�\<��I��`��|�sF�F��b��������q���������I� <��3'��0e��Rrgrzg>�!�:w(0�XL08d~�,�FC��a����
��{cf3�W3{n]��v�� ��tf�V���
���di������~>H�Z3�Z]��q������}��o�����Z?�F��f�����0�}�aLW)����72i|�d��@x`�t%���."�$}�[!0M����)��O�A�Y�����]��2�;��o+<K�w)�nC&I�a��U��P��m{qd�X[*���|��V����5��g��~�A�F|�<��P���7L����3� ��WR�k�1�Vd9�)��8�);j���)l	S�1����D�j�����$��<��,kY������qdg	�l&�,Lp��������i�^��r)�a�c��)-�����A�����@W������@bb�_^N'+���75��g_��%�~ig�����\����iC�ga\�H��f��������$���{=��G�5�E5U�����T��VR�+�n�����w?��%���(?d��Q������I*K�G��"����B�c����g���x�1��U����b�����P�r'� �3ze�2�BR��q��w=�'��CP�l7������*~��[���m���JY�Y��-ncK��R6�l������B8	�d������2�+L��yP���v-n�C� ��Z<5��sT�W25h"+��;��
z	A�
�E���O�
��|$3|x���m�������9<����C��=<�g��dCo���w��C����C�{����e��PJ*Q_zx�_���G=<TZ���CUROn-mjVR�*9'�J��W���\�J��W���R��u��R��������������7�z�\�}��9,v}��r���9�,B�k�z����z�������v�%&����e&�+'�_9��7��N��P�����&~^�"������P��*�B��aMfL�����de8�'C(�5�h��<U����=�.��Q	��n^N�J(Tv���,�����$������M�����aq�[����H�����e��G�xT_��#��_W���"e�� ��$��T@����*���E��,�He�&�Udm�0FA�*R�HV�$;����l����/�A/3�H���}W������X�j#�������&���}e7idf�)9���*��$7Pvk�U<%|Zn�V^n����b��|*(�N
~n�r��An�����r����B���b�~?7Pu�e���dn�L>Es��[c�ryw2K���
����
T�8/7Py��Y�:�(��hn�r4��@�gc��@%��xA�RNg�{�*����Z�>���#\��/x�$CSKD�o���34U��o���"�����
���	����y&����P�i%[����N�sZX��������\��H�����`%c11X�n�LV���}>��d�g���y�u)?[��db���O��<��l�kP.IV�H%+�|��|o�zn%��y��������`~;��`��db0�����-���Nl<��3'��0e��`rgrzC���m����6��U"1�l����-c�J��JJ����$�7+&�x�,����`'0M�����m��'���,U�����.S�g�������
��~�!���M$kI���O��$k���$��Z��`�����Y�$Ki#��J��<1X�AV�,���,����D�q�D����;$��%��%��Qjib�
�%+y�:�,y�:�,e���l{��6�����j��`�G"31���OO���$�:%�m�v�hb��K������W����`��Y��$�3��)'1X`�2�e'��m%1X*�2��R�J�Z�`b���U��,�,P��`� ,
�wD��`�0�����$S�����g�6�)l��m&S�V�������Z��F���-er���_X|�3�%�������La���)���)Lb#h*gK�.g&�4�K�
��}�C����j�t�g2��+R���(�Jv�Tu+�g�*L��[Y������H����U���%�������i����I��7N��tI{��$�l����S��,��/���,zic�$�m�f���6I�b)l��p$�@���gQ���yjE��H�8�
�����mY]s0��)b����r)�e0����1�b)�o�NK��0i�n^�V����`������aI�eX:�%��<���b4��1Y�=b���r�!��?�/��;�5�j�Om����?��iO��k�p�s�1��~��r�0�<�!���z���QU��
�E~���sbr_�V_s&�3����g����?���0x>�Y�+�au��l�9����G���|}m2s��2T�j5�.R��7�pE�N/S��po��=��!`|�w�@�"`��X�����b(������"��oF��E�NW���tM�"��!@#(���Z���t��^�9���v'���;m�?�:9�����O/��O��i$��J�` 5�"��"o^�rp�w(����O�
�j���e����Uk��D!�	��8������
��[M�	L�n(���_�T �#+v�A!=�O�zk ���������������a�� )vr��a:`��Z���~]�
V��GG�3��==xyp�U�v9y�VLr��`�iE��\��"���&�2IZ#���%(�����<a�Z�?�X��1�m6yw�,�]���	��0��K��`�M�Ba0���aY����`>�NF���+d_����f��i�5���^����������X}T���t*B�n��|.�~��T1���&��g�D
���A���98jA�J6q���b��O>�"OG[���D�J5�C�NEd�O������� ������o��d�`�ga`���A���M����z��ITJ�������7��m�mk��18/_����ACx��hC�h������h����ym�4l�&x���7q����|�:�n�[u����dS�����s�?�o0]<�����pf��`�����pm,����4��5��sjt2�y
(��`}<�O��a�C���9�\`�����������r������"	{'�b����0^����h���p5^���-�^8o��S����Z
��f`0�a�����W�+p�Uw,������C�a}�N��%�\S@3'+�b������`�eE#%�KM��%����2,��@Q�a��#��=���|��q���h��Z�(O�� {3?:0�X:���%s����K�.s}p'����c����X	j��������7����a���qWT��Z]a ����w�t��������������:��b)��((|�Z��N���D���B�kT\���|������-/�E���R���s�'N�O�z�����������y�����U�b,R����/oN~�m���������<�������?z��$B �Q`�|����k������ND�o�7%��8>E/w������ i��[$�)
r���fC
r�4Z�T���g(�Z�j�6��h���=E�����3��k|��t��2��&k��{����I�3cuu��Y���01�_�q�h�t���2��m�}a�<#HSs2s-\�Y���c{����g��
jL���I�oo� �2�q�1< �7'G@ t&��#�yk{M$��4�d����
U����a_D	�!.{<815�&�Rg7e4=���
���0|6`�8U@����
~��q/�
4��{
x���K0����� t���s��I^�j%[�z���V%�}�y~��(�8���2	�C����#�	�[�����L8>�N_�b��AQOq��(��/bY���
Aqz����l�q��gN0�w�������:She	~��7�Fl�0�0�����'��D
y$��6����v�I0�#��P�'�e�W?�����'�����o��"��qJ��h�-��q�����*�;7��.�����	�bW�������������X!g�AX^��C���w5_����.���p�g�S��g��{
w��+�^ ��fx�i���C�Y(�����B8�j���a�9��.����X��|G���������O_������/
�D!�BI!.(���U���x�
�}�"�k����b��v�\X�7���)Kc�}!`�V��5��C�Di���{�Qm�Z�'|�5��m����&ZI�(�t8IH��M�Q?C-�o���������w��N�NWd$�|���?}��/�_]�l����tF����������/x�����g����:(���%Q-i!j=5�$��<�p�2l��V8/�^��S��R��+�#�������B<{����)��
�5����vL�(�P��F
URA������&KJ2� a���^��p���@�>9SS�z�[��>W=B\����W�*U��!-��N�&7������`3���&�J���#���9���_�f�bn�
����W.��4�A��%Q5xM�R�<���;�d�y?���X
����`�mH����2y-���q�$(�x
�����LE0 ���"�����7��@Df^�>���4a�t�J8O����L6����X��`k�@/%��<�)@3�w�-"�Oi.��}x�0"Qp/E!\.��C�U��bN�!�Y���:=��=��x|��
�
>����+-i���$66/k���.K����F����k�P��1�Gb��J>�hy���X��7,�#�u;��m��I�4(?����-2�#� �������|��^�9����n)�
N4.`���^�\��t��9T��� sR�Ej�����0r������w��(!e6��%DU����;���MN`�90$?�=��s�@��p8H����>0=A'�����N��!|j�I]t���G�X,��A�
#��R�M������d�6w�p���A��c��b��sr��	��~����IRq7>��t)��Z����W3���b�5�2�������V<T@kFK��{��+q����/��������W|�L��a�z&��V4U
�v��%,��� ,�Q��z|;�c��|b���Gc���V��f��4����C%��k���7���{��)�5�gS����[�,
}����_g���O2��C
Ql�e*��e���@�h5����'%X�5�k(,���)��CU/'�����!(�bfa�kD�eDD&���9(��d?��1����G$N�"&������?�0���"����-�k�,�r��ph�%Jz3�4�y�5�L"���r#�b���������gly-���������@���Q���5���}C���� ��S���)w��c;�U����+�W?��8�����P������
�P�0��T,,���)��
��b��1��Sv:��)��
��p	,����O�����a��^Xn'�x33Q�A�d�`����[��G�z�7:e��6�����1��'���i��*���4�}��\y1LB�.�o��/���Gb�5y�;>���
�����#7�����{3~~�.�����D����&��F.�)@��[�z_��9V�k�N��W����/����Z���i��=�m�6�zT<J�Xl�����T�./���2������f0�&����;���;7/�YC��r3Y�H����'�D���[��x����7����	�qxdoN�\����,w��i�/V<��N�6�5�_��<
&)|���y�o�����]po�r��'\
��_��7��b���^�?$�Z����R_8h���
�����>�H��G�NF�n9(*&�1"�gt�q�����`'�6��(�`"�h���e��)�&�yU���8k�`�gdkj� B���Y��a��&�CE�������Q�����~1���>"�R�
u�Z��'`&�x��L���'��`rl>�	�[�w�+`d��n�x^����Q�R�1���k�����0�e���j!�q6?]^Y� ��c�-gfO\�������F[�u���a)�����0�G�>��������,LiJ[:|��O�p.��0��XV�Q���r]T�r�]a�)�
�\��������lO��;H4y��"��^�0�9�
�qGc������6�	������	c�"3�������;��	�d���>7"�mO��	L$Z��Y��Q�8-��c�.��^���{�v�zS���o��$��z�+N�Y2�-	�I@4�OU�{�iG ��R.�AW#��Qm�W��X 2�Z��7�x���=--�L����$�����4C+4�A������?��6�������a�"�;������|��������aAYu�))	�M�=�������a���?0u:��+��.���|��q��+\_�8;s��;�+��z�:6���=�L��|p
;�^���!��z�<t0�6KFfD�#�o�X�+"z`�g1���ah"$�VZR��rYa�(9�\8P��xc��'�^$N��i�V"�R��a�60���J�-yZ�����h�R+����0����0�H�$����V�� |�]�V�.Pr����Ax�GT�B�
�)�J�)I1	�/�����W�L�;m�F��:|P����>�A�����t{�a�Hw(�W�C�v�y�N+��������|a�asE���o����}���$�����(�E��R~E�L��2*������S��<��X��m$��>��,G�_���m��6�/H<?����M��yvx����D���{���j����gH����G��#>%����q\����{���p�pq����A��pe��/�mpA��6������P�n(��}��>�]�����Xl^�V�I�?$�4T<zh�`��}��g���	�&FX�jN�9� �$9����?K
���G�I{��b+���w���(�F�zv�����6=I�������a?>���%�Ve��|��om
�P\N�	�W?Ef�/��kH�\��A�M�������t�����J�2/�^�"w���n�e��[~ZJ���~>�<������%�+y=���n\�����(��tG��u"e�+��c	$q�������D�������.o�Z�y	2��8�q���^��������"��eN���3���6+R)��Y��I1����X����d�1Y��^6U�T���[���Ef��~�4��@(��ZE�x^?g?��KF�xN�
f����^��M��	<	�y5�{�Y��^�z�������Y�joz5�]|5�������bV[��
ykV�.X�cV����So���KU���/^����7��L���ZM��KfJH{��E3�)#���������,x����Ea���0p��������`���_��w"ik�j{������u��8h�u��n?��q��2�� ��~�c�0ry?�y�0U��+�s���a`��6+��p&Vg������+LW8iPE,��`/����}F�@?����S�g4���S��}3��P��">�������"�? ?���A��"g��;!�~�rH��d7�v�qxt��Ha�,��l�Yoro���B��r���h�|��{���)��2��
�9b�����5�anhT�w��5�����K�?(<U
m~_��a�B�SO��$��9y���Z����@�'���[�a�����?����`�������d���]�Jta�,����0q����������������nm��&�A�=f���X�5i������k�S	���B�yF*��JO��<���U�/f�d���HavIV������x�E��!�����[$q��!f+�B���z����s�t>F$�BWF���	%g�����S�\��d��w���hpZ���k�8Oc�(�����&��0��k~.�`
<aO�'�).��=Pln0eB���E�TX�|�������PMn�}NMZ-���6���0�!4A���4�	4��\�
o_Q�Hd�
��S�V��S���vQ�)�"Z���:�)�G��U2��5tn��)A����^��T��o�,����CpMO������wbQ�NG�0�Z�%���m�(����
G�����h��+I�G�j���_rE���i��
�^��{CE<�$S�J��8��Yx��>�����\"BN{J&L����vZe�q'��-q�0�	mjH��FD�.������W��
����C��v�E����
�B���1\@v�F7)�=/�=���Ho�T���|����'��"���1�b�Wy�K����g���c?�V���������x&�t�Q��	�?��Z^��,&��I�7�P�i4��B���N���G��kiz�wU�� ��!���1�!m2e0W�C�^��[C����p�H����

E�HU��#�Bp����Q��G�C+6����%�g3�@;���#���+�S�K������l���+K�+���h�7��c��W���w���SkaQl�w�xb5Y�P�i�yd�����9�i��?�z3���A�;-j�Z:]�5��aT
��������������~�I@��EW�e*��d��Q��<D��=���y|�v#s����H�������ckh�$<�/2k�&>k_b�~La�h�V0���T.wK�q8w�g�F�B]�k��D,Q�O�`��&���j
�[]\>�X���cQx�*�a�9j��D�Ujb��v�l��������:P�&7}�;�
>�Q�O��E"�2:��k�������O��t�?��;����9-�
j;�k�a$K���/ZA���T�I��$8�X�	��K�`	$��6�)f;���0
��W��S�5.����|�_4=
���/�u�#Pc��#65�zy�O��1
':R�<�4�6�D������UaZ�$68�(�E�������I*�wr�y"C9X?x�{�o�A��Y�J��O��R�c�	��q=����!�5�;��Lh
C�����R��r����w����mO]�N��qgYC�5��I"�R���1:,������TQ��N�%�v��\;)����|�<m�%>�.Q5\v7���Z�z;{K�XX��`6���Qo��u��������</��3c"<4./�7�e.���IF�o_LL�%�bz����&��bE����4��u(?��V�[����p5���A��b+|��gN$��'��9����p48><8z���/����������v�Yo���nq�@��y
n��5�J��*�A�],�I������i#��Q�f�U^,�H�"<�b�����(�������v�*��H,��:��w>w��\�x���B�C��DS���������NiA��N��:M?���^��ev�mQ�D��O�Pw�i
M���9���`�!��@b�X:o�������H�������9y������T�����c�����2�'H�����ASl�dw���]w����0��VH����B�ko^�x
vrr���'}d=�B	<3�i^W���!O��������,��7��K4<P�4%��r�%�
�>.J�M:CF0Kc)xW����aW�w�+@)�Y�	(���*����/�O��4Z�'W��/7�
<������(��]�v\��L<s�h�g��
�a�4Km�R3��)����&�����E�
�<�8/p6L���|�>tIk��_�$�y�K^<��d�+{� ��Fc?���;���
����x�I�r��x���+Z�W��x�+�h@�����f$gV:��i$�Kg�X���>f����M���!���]!��}o�����������7yh�����lwL����P����/X�v�O����x~������������! (,��(�~",��8�k��&j2�RQ �_�'�k����3�t�Tk6���_��v��+\n�L�z-�(D�:���cP<��%
��g<x��JB���X[h�<�-J	mQFh�������� 5ySLD�0>�a���^���Y�I�+L���4�����D��w�AqQ%g{�^K[�2xQ�NC���x���w�^CV�Rk��R�������!<�� -�=|p��[p
;��!�>�{���j5.�7���qp|e"�����[�N��`_�b���7�c)o-"6����y?8
�pu��$:��%��|� tq.���`y@6AB���i%����'�����=��������bt�����p"�z�k��9��v�D�����B���_���%cdHL��/�e�a��)b4����������pX��e�,�gA��iR���>K�&�Vy���
~p�{]/_�~���'��(�B�z;y�������}������s�a��H�1�����I�1'{���?ON���L��.�'�\ie���:�h�P�7�O��IQ���)��=�8�&y��6$Nu�S$��N�(������A,"��#���������q�9�a�)����6�9���f����_<S��bq�3����J| ������~�"�=m�0�>��<�ypA������~�j2�5���h�+�,�*W��Q��yd�$������n��
�u#o��p��>.`�xO~NvG�N����pv��;*�{���+���{��3������������F�~�
m[���[�=�_iW���*�84B�(DQ���d�N�?�����e��ti�j����77�%?V�a����`})jO9�Am����N�����]`��~�]����9Q����6���;�{Q��k�s�����l9����� �P?�>���g�D��/;P��8����i9�e��c�S�s�mi/���M�v3���.�M��=~�����������6uw���C��~������qqEUT���.�\��zp�z����
X����_�����(yO����1B�Xb���8;��P�����*r���������nw��{q��M>��mw����?N�~X/���12���+�() ���kx�7�-��_9��������K�����XQ�%_zd���1l�a��t��"o.�5|�W�Tg���u<��b�]���SRY�
s���?���=�$
��3��B��d�L��:�a_\G���
��i������IA7+�\���������^.�P��x>�����d	p]�s3��]8�D7�P�3����q���//_�~*x~���!ntQ��H�v@��|��M����X	������}xS5]i}u���/�����R���W�e�%�Ow�����1)/�?�X�5���t50^j���&�;H
�o���������i�������Q���F�~4��=����p���`���G����!���.�CQ��;��_m�m7{w���E!����M����';w��O:�N�������o����S
K%G��Y���7~������8���)�����HQ����p�E��_�� "�t�8s�#$Z�i'BODv�pFA������W��u���f��R{�^�:
A��4����/P=O)�~,�c8����p��C�>uY�W�?�9������(������(����5�E�F�+���4ZX+(���p����i��#��`=�����y��,��,�B���7#/G��s����^���g|�������1��(xx?��#�6�@�^J�h�8���y9=&���Cqv�D"$��B��S����FMa"����e���K[k���==8y�s��C�h��0%��Y,�+�"<�9��8��vS�����|Q�
a~y���h�3MQT���[�_D�9�O���9A�
����sPM���c���NO~>z������"Y{bVa��~�*�����n9"��]���{��N8��q;iy��e(�J��2~�U.~�����6 ����A���4e���h�RF�%L�����:���8��m	��f�q�[hz���.�+lF������R9�D^$��{�V7�	�2�t��H���T�ixW�l�k�{�o�'��B����tZ��1���.q�L�)���\��xF(���j��cX�
lGaB.^���&���$�����rEU�E��-������������WU0���F�Zl\���r����W����
���������;����Pe��U�X��'SXd���P[%�D�QV�;�?��v�t<���;y��7 �6���jw�=;�������:�+�������zO7�y�"��<�����"����o�I�w����E+�a��7����1x��`������.�N��`�/��%f%W�����t�s�����X����0�|k�Apq�`E�b�s-r�*v
�=���0:��K����0T��	*+Lwx
_6���{�	����9���wf���'�Yp���//N�N��b��������qc����_�vdR$e�^cY���J�io��;�7��C�%�l�dHJ�&���_��
����$�~f:b���/���^������Z_��F���������M#��#���A�"��r�U��S�_��&
O���1�
���
~����t�wx~����<u��^�,����hq����G��,.gY:B��4� M�w^��ff���|�q����l��g8�*����]i�rSx�r��P �n��@���=����c�j�_=o�F�w�d7[���%��J�	a�[�8�vbn�vS��V7?���������������q�x_��������E�����v�����N����0z6f9#x��9s0__�3)�����$tU[n2�U��&.:!���(��Y�}��v"_2��"��~Fe��������uF��(�r/�7_�OR����f�N����n���{�A�������%����=H�L5R��P-4�O�pa���s�n��Ddk����������y
^��5�aq�,�	��5%9���	��-2[c�'Pc�e����E
mM��Y���V������UxiV���u��66�;��Q������L|�|�[��j��E�������'�����
Oo��L�+��MRx*�V�z�t���t�/'����J������@z	��q:WA/�`z���y?�a8����?�G�����X�e<@3v[w7������;������~�l������la/d�6~�6�'��:���k��z�����-��<��>���T4dN���@�y�L5BbY8B?�	>D�-�'[�i�������-�i��1��
�������V�=��~W��1T�:O�V�Ef6�EwA�.�c�bN�0�?��1)<�Ue5LUNu���Q��p�����?����K���.�~��
B��M�	����]{���6��z��^���{
�6V�zk2�R}���P���V������h���<B�������3��i���"�@�A�l2 �!Xo�V�����`"���`����G��jK����������3-2��h6P�����x���_��P�c���V7���n�����u�K�r�o��[g�F�4���{���~<8����hp'E�����u����*g�`��>�J��V�'	\�a��<����-!�W�+�\O4v�W�Qt�����L7���Cng��@���_x����/��
�������&y���@�����i
�u���g?����r��N�RS����s��;5�U�'����8,NB�����R~�
sO+=��-9��Cw���7��n@5�����������*l$�3���e�2=v�*>��Mp�rYPa�������x���/G��G�%����\o&����'Z�����S���b�������W+/$���87��l��'����4��7��6�M��+���V�����.�[��$�W�	�f��g�U/���m���%wFK?��\��K���S��b��=%��u�CT|`;\��,rlV��
0����Y��j�4�D�V38�����rh�.���**,"��,�)^��kM��e�����~4�0"����G����~��s��7���F����O�_��i!������(�V|b-+�y��&@����lK����H���(`]$�}��D�C&O
��{�11���������q��lQ���~�2AM�#?���	Q����#E��~8���OOw����S����FSy���������.��7����q����U���
m�
���0�R��~�:5�?}�tJ�kp<��c�pI��=��h7��� '������32<�=�|C�l�[�������sZ�E��J�N��E���P��3-P@�b��
��r�~�Mx��Ckr+�5���v+��n�J�q�5RoM�8R�?�-�[��m�L��L��l��X��~")Z������=x���I2�B5�%��AN�~#hM�6�������������O����|n��1A��9�Taj����DIb#_��|�:L�C2����Om|?H	mY��nS�&T�y���O��:W
S���V��c4dRpl�Gs����S_b����0����;��������-�27������*G�6�?�m
�nD;[��\����|s;�\���r}��doU+�8�s���l�.nNn�������2���g����6�5��aC��YL��������;���<uu�lL�\�.c���������\�@�+��#b���������+��������;1@/�������#�6�Ve��B (�n�y�!s��]a�;������T����XR�Z�4��f�m�wP9��$�2�u�u�1���m����JZ�bz���w���R&�;X�&�trr�7�������VL�6OS�
/�P�C�i-������W����>�������T�"�>�f�#��7g��c��0�'Nqs�E\Pi������;�ziG����E�|�e���
��L)���r0���� Kq�\R���D�a
��lPMlN��L������Q �X0}�:t�
E���2dF�V������1�j�@6&��������&Y\���g��V�T�b����^D�/�EVkgU��cZ�P�.<��
Cz@��6j����L!���3����Q���g������2���{p,�L0�V���~|D0B�xc:�
�������H��L"��u����v<N��g�S�I~�U5a�`�!�2V7O�jKF��
7[*���hZ�>9�M{�y��\���i�&�Pt�8���D�$m�J�ooV�p�b��v�-����6>�{�����}�����z�
U�k�C����J��!e����`(��Y�:��,7�R��C������;IF��7�1���a�C
�
����`�+�f�9����p�K(i	
.�CB�W��GI�	>��|uFCRE9u
���&��)��0�)�~��+�3�4&l��s�����9��@��K5����8��x�	vo�Q�K�����w�~�?���Rx��c���W�|A[����n>��h\O��IDX����=���((	.���k��G��`
�+�s����$���#>eZH��l�C���E��ZX��S����&-l��K�fP��!H�2�tV���B���7U����7|S���Y����Br�S#9b�osL��P�����l�#}������$�����6�-DX3CiXKI�GO���Ix(�����������n��mZ�u������}|��L�s3c;�xj�X��wM��}���@|$�@e�����*���k��5q}�6����������"l���]�S�2�8O��_c�vf�h|����<��+n��%����/�z���������
��8t��[��;-�
���9�p�.6"A�q�^&l�`���o�������&�F�g�a*�d�4T�������6�X�n@��lt �b��)���lX�r�xU����%6�8����,
����x�d$�q������@B�
�������uqi�~1�a�g���q�P��Z�b���.�!��B�IW�E$�X������E'�����T�0k��S����
I%?}�w�����QQ�CRj����VtV)wU6*�U��h}D�[��v���/�EHv�����^<~fO��2J��u	@p?��e>`�J>`e�S8�D0(��/i�z�wY���%
�_	i�xxi<���@��w�W����s8Y#?�����~#YU�f������k�YI�1�����=d���(+�����?W�����LT.`\�6���;C���J�]Q�\��:��X�V��9*�f�@�������>��=�	�SG/�T
cUD/�O����(7]�O�"J�����&���Z�f>u�3��	�!x��UOD�c���q����y�%O�O78�h���[�����\S#�`�up�3x;����������@��_�����������"����#��x��J���i��QN8���Pz�����AN t���5���p�ui��0e�<4�m�q3*%Ah@��+��S`g	[T���w�f�����XV8��C�e�������&#^NJq�u0~���|;�������}�'~�"��9W9���`����8��3�hde����{M���*c�I-����U���������4��3���mD��z�������F����O����4H0wN�W�S�����M�"��a���B�SyX��m-:�d�o�_]���1|����f������1��E�>m�Q���HY�U�~Cz����������9*�
��R�V�'�
�H��v��-�nGM�"��$����S���Ky��"��C���'���_�+�u��
�(T�������}b�������Sk�r����
k���w��+�	�����DUi�����q4!�����9T�*�X4���X��������r�Q�=��-v��@�@i-�����@6w=b�y�����c�g�e���k%P��*-h������������;��q!aU--�*B9�Y�:FP������/D����Q��w�M���&:�xc �@���*#�E����E2q��N������!�r�y9-s)2��3M���Zo[n������7�
r�U7H������C��Z��A�oCv����������@�E�if^�aUD	�����F��$T�v�j�\����[su�B�w��������"F��4OVtNG�P�u�l8�e4H�Y��_<�{^7�Ek��2�UX,������~"��{w��h}���fXL�q��f����j�!"�r�L�q�x�1'M�8 �h�!�Xv���������/
�
��������^'���-Y�on���V��,�������\S|�Y%
�B�1�,�{p��{�5�x������������:����_�c4��>
����$>��_�Y�����_�U�����b�c�\#��/�F��,e�b6�+������m��������:SE/I��I�
P���d��f�K�y!�h-�8��������mu4z���V9��2����q��c���O����@�^��l�^�},��`p�0��l��J�kO�Y�qx3
��;����k���K|�~vy�|�O{<20�����l$*����9�O6��+s�6�w�
J?F�,-���iV'���s�k������\"�k��R��.�[`�.P.2��f�����o���[�OP�f&��1����M9G
j�~>VB,��f���f)$�/`�Em������L�H�f�2�J-����v��673Lu����'��\jr�c������j����4�f���m���s����~�H�{����{����]����l0O0�������X?���3���5y�
j�H��w%)���U����t��VK�������<6�]�����|�c�u����p0#���C���*n��������X�����d���3sh2��b���Q?���� ��
 �	R�6}9q��@�>�D���v�����<�O���3\�6�z@������9�jG^��.C�'�s��8���>����J������;:<�
�m�H���;5��K�4�|?�@�k�Ca�Z�E�Y���r�[�_qkL�
y������wJWeM��:��Z�����yq�����ka��[�8���������|cc�H�?�
����y���/���e�W��(�ELr[����;��&�+��`�Q����X������h�Pc���Ln+Z��]��Fe����}����
_�)2M�N���h+��<��J�Jj-�h!xQ����sQ��k�
�^�uJH/���wW7J��x����Hy�hl?K�Li�|����������f��`�'��9d�*����MO�AS}u����!?��W�,J�Ng����m?H?G��B����
`������~�yw���&���LE�_(0>M�����2
���3Es
x���M�dn����Tr6�_� �T_jL�9{�N��4����~�!�&�*+��t=�����$��^:"j����&�#L��G��i���cimla^�������j�����������C~Q�}l)�}
���f�6�L�E#�bK��R�F���:�����G��(�%�2Z�p�>�@3K{X�J�����[aT8+���B2A���o���������-�������t4��>`W��E9�5^L�6b�,�L��n}��A�������4�@bmS���PA��g�7@R���������T�(����7������x�19#
������GM
G}@�;��w����F+rgsss��R��{�\C��K��d8����S��`�7�p�<-�i�t������
�������C���t~�o3
�����*�@=��%r�����>V��.��8���y�K ��eS} ����L�P��n��,���� ��MT��Q����(�LQ��j����k��O��2I�4���k�t�mu,
��i�����g�/�5o�V����v�����8��et���8�;k�������e�\��w
���}P{��Q�;�7vp>�{%VZ~��Y����	�ul��+���V����Z=^m�N��,��1�l���)���%r ��g����9�JM�dqSW0���k��+��K|06�$�@p���7���FKa��l<"�L��^��s��?����������Hg�:"%��<��zr>^~7���Y��9m�IPcL?�����'-�������W��[����U%��4��;��+�:�Io�;Z�rl�_B[�Ft�3a�w4��h�������.�'3���7��V��w���k�mb5�&������G����R�5@@
��D�Y�����������zO[����}�R��C"�]�����/���/������}J��$�v�o��S�������@����MGKO93�^	�'m�`���*#\4K�ff��X*}{�1W|���wQ�>�Yt��q$L���S��cu���7MA�`�-siLFx����
�v��zz��u�FN7�K�f�{����I����2�fva�g�����!	�������n8��4�L����W�	��I[���?�����m��-&����q�|8�zG�|�C���m|F'n��%��[�0��jZN4"��������j�F`t��0%�������{�?�^$C������^�|�x��[�����'�3����k5��EY���:��$��Y��[�������1e���n���<y�K
�K/����M��uE������L�P3�����h]0u*�c�������82U�n���o����!Q��kVL����-{�v��#O����_�B�x��O[�l*z���}���-���[�UY�u����rV����VUP0iA()'�CH��X�h��Dm���.��b����g���D	�v����}��'���Y�������*^)s�;�(��k�x�Z@�V��]�zu��N]2?�����/��1Nt��sInn~3��Q3���H���J�Z���e	e������	�#_�!S�o����_&Q�}��,[�_�N�d,�^��cN�j����X������L_g
��!�@��!��s��\]�O�\�����kP��s�,W���
���m������9lX�
����&�����'ip�h5��h94P��A�DCc�Qd���(<��i�L��q	8�8n��������r�����c�E�g�r���,3���)�|�U���z&�.�:t��+z��gu��ZJ�7��S�&������������5�H����i�t���4If��,���z����`5S��?�+D�h��X�A�$�R������p����<N?&0��9
u�]�1�������L�G���%Z?��0���uv�l�q�L��>��Ybo��g��O�3�A�������5��������7S@FCiJ�uzf�Gs��}�]�6��-&��1@_�M�*+'	zd�1�fv����jz�R�8�E�:}K�����"i���2�h6i5���S.��zZ����:N����� P�M|��J5"pU(��OU�8�b�?�x�.{�������2��G�q?�V\J��
xW�(�_�'��)@�2Y�1 ����D���k��\s��sgx>H��*��������gg�6d[�+Qr2��c�Y�����������F�z%��A�P=�fj�jJ�w�u?�d,H���"Q�*�w.�{{�E����;�@	g�d��*C�M�c?T��4�5�\K����go����62a��VE��&4��tNU����K~c�
W�.���PX$_0\��h(��t0) ��o1�qfP�`;|�s�f���g������*������gyO\���4��$��;b�T�����<���������~����a�E�u#���3g!������Fkc�D���`/g��*B78V;�/����-��	a��U3����jK�z8������b��`�kLO�B!�����M}9o�v�2���Fg�Q|�������_/�n���L�����
��|�=����[Sus4���������zL��1��C��XW�P������'6@#����	V�w �m��P��$J�c���p���`G�� �r��
Nh4Crr5�ew����A4]�������������g������$�O���J{����9���k#-�]�D�$:�n� �"��� /�`Su>�H����}K`���x�;����+�8�I�����F�h�����B. ����)����5��13q���Y+u�c}#���f�����&��I�w8c��C���bE����K�O��w�����k�_��J9:!WR����r�h�GaJ7�S�s1��U��>��|�������/Rr��0�S5��o-�:��������e�kq&�;a��J-t�J� �q��6�mg��J���q�d�K�&�)�k�PIWmZeR�w1�s�
�r��N��q�i����f�3.gJ��0�Ki6e��Z�$�A�v?����X�M�DP��E��GP�b
�Bp*�_Q���#X��(a�i�}�:�hd3������a�?�(k�&OPD|rt9Oz����������d�y�{��� _3�u���d>���OGJ|��
���������2�V�]\�k�c����������D������"�D��������T\M������u�g��}q����
<y���k�����h�i�G^���<>�@�Xfb���z�~�)b#�HT10aq9tvke����	�>C����7Dd���#v����R����������%��73g����n�gx�����]yv��l�
 <31�Q�=����B�0L��x �zn�!��H�nH`s��(���P�>��G���/~��������U�F<�����c�yB��;�O�<��q��'6���~�^�wT�s��f��(-_���!��r���}�8+Y-�AQ��d��b5������^��1�jz�sE���>���&��uX�����Q�.�
�O���=��Nj6����%��K����&u'0l��6���\�d4X�O������(��z�H�&j>T��3�<�|�_��$`�;��BwrO���9�b���p�"w���y6��x8�1�KwoW5�v�w��z��F���6&���������LZ��,��d����?������"OG��K�c���F��H����� `�����?-'���ff���]W�,^e�`�g�����=t'����-����E�9�����<��z���kM�����g�9���8��ua-�,:��^\�g��|�<��w��d0��������3������H���s�����������j��O�~�w2��1g�>��8}�� ��z3e�A���YO�w:A�08f@�\�o�!��s&Lv�n�����ph�}�l$N�|4KOF�|v
2��	tm
��d���pj�7;��w�������dc����:
b�#��pit<6�~�p�H'B�������N�mL�	��������F�Sp����9���.z�	a��;�_�sW���D������V��C��5��:&
�n�
��k�'�[d`Z���A��t<�A��p�����q��8�x�c�3�W3���\���4���p�T������1���\b����a�F������<eq���hA�������t�������Ad��^����*��H~��6��9��'�-wWhX�1e���Hj�.��>8���H���d���+���y}Tk#6��$��*Sx���� ��w7[�Q:������s����`�/��D�k��������+��K�QdnP��M[U�_#�/�V� �`��R������ #���"�bYKa�_��*�@n�oar~d�=yD~��-�@���5\uZ��	!P��7��>���u�[!� �z2N��t<z����@���y��r�/AH��<��S��D`�3)�d�L#;�����I��t�O���|��	�p��������?�X
(�A��������9� /37�8��\�*8���5X@�8�p����������o�A�g�S{�2s�VO^��~������/��CE�yK�?<���zb��F����M�7����n�b�W)xSt�J�������*�K�q{k�L�$P\���D��kXf��gjv������eF����[MHy�Y7UL�����?lOQ$
�t�
i�����8&#���8��q������j]���C$�|��n"�
�D��~v2E��#�_����5y���#�!�����Y=V�HX��|�
X0�`��h�Q(-�e�
����F���A��n�t2��x���q��V��	����H��e9�4O��3C��V,�
]���;',��l�d�S�!6�a�LA���F��������
N��_�|���'��U8��3�n`�z��V4x�QP��l!Q-I.���f�6m �6��lW�7-����2��;N�s�N��Q�7ka^i[��^������qbq��������(: mL��D
�>Z�����[1v+t�oL>M���$�
�#�����~���rh�?V��jEh+�����H^�Cr�{`�_
�j���I�s�[������v��}����zj���k>�`��Eu���PM���J�g��P'�w9K���Ko�������5����2�adc��������Q������;�/oRR���3�DL��z�����&�4�����&A�j=OJ�D�R2>O�4�f������������=)�@cAcR�e��WM�s#e��|�>$�K�T^�:D�{�U"�T�}�K�w6�f�Zz4�y�����������Y	�R��v�{!K�4�)Si��NU���@"�t��+���������F�3C�|���D�;��!�[�C1"4���2��V�G7�&���v;��������s���w�����{���
1.Q��B�^0��l~�K��z�D9
&Ku������-z�����|������^�����iO�SZ��oL����^��������7���+�J�������G&���W?<9|���z���?�y��!�����8�/:g���e������z�������+H�s:4�����V���j��������G���-c��l��Q���t4H>�;w��������������W���{w�������W���f���V�������-��R���={s�k�u�'��3�K�i��>�����Jz�����Ea����^g��U1�� B�[��Z��H�[5������Vcj����Z�g$����u��&5�������f/�#(h��M��*���O�M}L��s�7���+��f���	���'�&�������t�)�	�:8���
Sw'��A)��4V}P�+p��*l��a�/Jf8�Q2��$PX�W?P��z�4U���A���jiLq��(i��9�O�G8A00�76�������7i
�0���Z7�19������
�����i��?���=x��\B�1�7���������kv����g2�\=��z2��.�k8}5N{'G��/�4���q��@�:���;��J�[ U ��@���
��v1^�������v�8���j[�I����V��nG�"�;������5�:��
o��IaP���Rb/DCC�!������1y���IF�5�
�!��x��N�m������
X����
�x.Ml>�&#���oy�������XtE�b�=�A0*�N<AW��iKxy�U"u<.Y�)H��x�
J�p�[���o<�)���Q��x��=���a1#
d�m>�8�_.�F4F#�����������L�������T��d{�T�=�[{�T�M���z���N�Y
��2���&���Bl+�l3�G���� �j�J�#MNI��q9<�BGnTt����4,��m�6�-������
�g�lq���
tS��h1������M���h�](���[��x�(�����l�;f����n���'�<yg�Y�	�k��[�	:�sG�Gw�$�PU�xE4�;����Q5K���(BtU0�G<k��}J3wW)2I��������h��.��F�A�����<`Z9���K�Qnr=]7\��c*�)�qf�(T�Q�"9N��S}T�Z����$�)�_�
����3�Y*0�S�.���eyN��e�����C�����1��5������d��(���+��H�>C���_�O�n+���L6����'u�P�/|�vM@^�����3�\�O����h)Cx�����d�7��C��aT�7*��C�����b�����"~����:�`-������(Kx��^��q��a������u�C�����.(6�S��j�J�"�'c�F��"g4����u�Q[l�m>j�E��P���(:�+ ��N'����8���NBz��+V���=�o�2$���U!?K�� AW��/��Et�d���-�$�����F��)����LnFA�*���c
�_�X]Y���S��2����_��^�,xuN{�q�e�Le2�{��,!0JNt��Dz\q�	�W��}`?�zC��'"�&�e�p����W;���D
���sX0����:s1aS����'u��D)�IX��l^v����>�V��R5�2��������ln���X�s+:5���������N�!��Pew�$�|������PLz�S����'��/�X���n��if�@���>&d���8�Ja�����X����,�����s���]f�P\1G@m��!�&��H7�����������Ow�����n����W���Zt���I}��p;�(�dv~4�O��|�������_�<pA�v���hU0�9�����
<����	+yQl���d�B��Y@��)�"�e�����=��If`saMn�ftn`h�z5�L?���	O�� 19Se�������+/�F��
�$u���F��kmU,y��KyV�X����Lg���b/mm��KH�|<M�����I�����K��0��3?@1#�yb���L��<��oS�F����LxPCu~�%=��OK��	��k�xNfe��Mr���Z��1���6���<���$�H�������
�a�/���W�@����4lv1'����s2��c�w�+6?
���^��n��y^�fn����?kl��nm!V�C�yBJy{I4��7�4�8��.Z�G���{��E��x����Dj���-;NCTs�B�_"��<
�� D�.�����`VAR���������%���
�*��+lN�lN�K�{�r�uhz�(���YA��v��.~�%�0�w�,�]&�"�p%
?'�5�
>��h���(�����FH�Q��).��+�1�=�	%��'�_��)@�
���"Y/A%a��SU��Y���&�ph��x�����E������W&��z���U��#\8������`a��o����9i���|~S�����)x�!����T=�.���bz��
y����]��V|38��������A��2���T�HV��N{2�:�
�l�sw|/
����0�k �Z�t��<��h���&yV/U��6qZ�gGI6�0��yD�t�V*k��j�(=��%�u~�/c0��t4|���_��;W��4�\-(����h���v�}_��W��W^�\�����t6�Y�����W(+�������,C�V���L(�=�G
/=�C�����U��E�C�F_���C�Lk8�;\��hJQ//��/����OfW��?���
.<��.��!�����+�Eg�E�8+���#h���m���NA�k#�R�z����L��&m��{�����ee�L���E��
\*�m���O�:����0k��V&����6#P���S�$xxTme������5m,�s]3�9����������U:�������
�/��P�A��
�Iop�f*p2c���|xIyg�d����Z���"�t����-�na,2����z��#��q%`�M�\�@���+��R��E:>���c
���-��e`��_k|L�a������E������A��������5����,?�hAne���%V���AA ��r	z4���9/��:����2��~s�L���A�{O5����n����3(w�2���X���5�����eUF������o���J����[�0u����(��K��m��yh-�9���F��C��D?h�`O�F����G?-O>B���T
�#.�9��$��K�{�����������,u`
�*��s[~��g���4��`��Q�����C=��*W���(��A��Vvo�����>����~;�L
�E�G����ox����Y��(�G>/�8�_�4�y�����;��\��k��m�R4NQ�-S���zV��&���y���H������@��yr6O{�������_�Z��.��5����x��Ew�Jggr�����,�at�xN3l�e�C0�9{�N^$X��sw���eR�6N�Ad���d>�v-���,*��=p�i�~��[�QqarQ�<'+u������if4�����,��,��.���|zm3�[�u4���}a��F����fg7	wf��`�\�P���A33>�\�=�������}�%�q�+o���D��"�����b_��w��{3%���J��T����]���-jS��$�p�6D[���w����pG����WO�=.�����4����u4wH����M��5������ :���]��{/����4�����S���:z��
E��_d%C����>�2m�����^K��#Q����2Ub �I�q�]�	'&��.���R���k^���J��>������B�@e��R���P�tX\�����C����KK�~���
q�)�D�LE���XES����]x�q��"�V�5���R��q)a=��W�����eR�$n�E5c�����4n�\��m����J��%���5�t�=d����N�;d�6�������*��������3�d���q�����%������(j
����1��,R�
I�P����$%k!��<�H�K����������n"<�@h���7�&�2]�@@e%��M�dF9����Y�aQ�3�RC�W�v8�e�"�u���(��qT�	�*z�����3s��N1�g�N72�H��a�?s�#��a���e�k|��r;\%Tsn��V�9�l-Q�*m)��:s��|5�Q���3���:�gv�3�J��x���,���dw:'3)V�TMy,�����""�c����yyT�p�3y#S_�Jz��O��"����M�g����� ���P��6���CM.��d�=�����	S9��|�+�{�i��,�E�� �1"bU���	Y��s��y������LY��(^�B&*t�
�(.�{����\��%���Y7l��^��4��Zt��������%�-&x}E�g��`�u�]W6�./ 	�3Q��q��@v:�T�ro�N O*�hq*�w�t�i7+����<��H�nBts�:
��p���"��)�+��%��	D#���Qg+#$������]>�����\	5��GC����N�R�XZ�a�m�D���$h!�!mr��*�5��[6�.�$��g���t�3��L�gR�
���
��)����;���������|!4�5	�����7{�+YD����F�-�j*���nSf��v���9{�<{�\�R.���!�%�T�j�6����^�^~B��I�"��&�Z%�XY����dT�'�.8��A�GR����>I@N��+�K�c�D[+�l���>�~�����������b�����o�\���R��]d���������z�����]{)^��b���p~
��nc �����FlAfg�h�Q[�=���rb=�k��6�'�����,�eY%�}����%�z�^�K����t2o��<lW��Q�H�D��Y}#�1
������`�
VFp��%1���2��~��Xn���\�?�^N���
D�7���7?}:��{���ADJ�]�X!��DvEJ�i�*R�@dY��9Yhr�#	!��#�N�_/n0��08�*�Df���T2�
>RH��p����/87����,�����g������/���m����Tq�6�Z����ntw�9�����G��m^�(�C��jU�	�DF9�>���L��631a�m��D���)>6I�,�n]q���$����7�jA����k������E��L����}���O.�
34 6yt23�3n{'��:��S�t�(3*��{����g����H�@Z���aH�yU�\��V��d@����x�f*��M2����Q�����Q��^���>@��$5��j5�����|���Dk��:� r4Uv��^*]��������=2����{����B{\o�9�yV"sZ�
�_�]��q���
�W�3R^�������@Ry�I�}U�#�^�/���d0�n}u`-�jq�<�}N����w������w��u��u����@������P:����*���:�����6�RKK)F0n@/B��(�.D�=�i�:���m�NR�T�#k0/��_G\�v�byJ����%�����,Dt�!��#��[)�m�����zc��f
�����K�+
�"��ZM�&��,�:���pU��OZ�VOr"^VHrR�:����G��A�El/�������C��C������R��u&������@�q-�Am���c�%jG�*5\���9�:��GX���ak�X6���!�i���6z��^^u��.�;,�T��X�]u<U-)V>��I9��(3�	�cG��8)�W~%/0����b'la�/P��5������<�������T�S����cj5�)i?y#�������JU���d�\���\a�	/�"J?�-���S�Q�U�?N�_�p��JS��B3(��^�`L����)����{���{��H,t����V!�:��'%��[���o�p�VYhp�fXH�N����)�e^8I���I]&�WW��U�P����Pv���6�9/6�5�x!9c68e���7iN�����z�{���`���O�i��>������&+�:�s8�S`��WcM)��8�W�!g���5�&��P�Zo�q�|8��K�<�g����>�+��!���>O��~o�?vZ*����E��;F6|�H������U]#W�Ow�|��xh������@D���SQ�w��f_X����Z��Y�WXm��%uee��<�2�B��!p �Im�%o_��-������QT�^�C��l���z�#�2Y�1jnNC�Ul��p���Q��Lz�en��k/�U�\�[<Zz2ZmCy*�	t%����+�����_�M����\'w!�52�o7�^fnE���8�a�f�qC9�	�e�#mx�����
�����}�JEq�Qb4(|A��M�R�Rc��������4;��^t�r<�(y�=�����3���N��w���Bs�N�%#Pn]5�vi�"tS-� #Z�����H���bJ-�,
��#N������&Q�|���72@Z�����i�>���j�|�����;:�],���#�#���xH�p���(=��N�qC`#oW��]���g�<�z�@ks*�2�|3I��/x@w������B�������������|)]�k�*�[o*��
�j�.c��%z%3���*����1z#�M�����Yv�e��[Y�$~K�(q�<#��H<��z���-0y������*�����kH�����&.�B�=�dYT��gl���i��t�qG:5I�7�@3�����R�@N�+
}7k����3y
#��/.K�T��]�T�Y,��]���D�v[�)[:Y6���+��.C�#P�q��*����S+��F�F�M�
�~5�Lr��F��+����
/��_9Q���G����d��/��a[��Zw������`y7����op)���+wvpo���`�,��}Fat�C=�shg��M�<���M������ze�����7��c�@:�~%�.�A�$l�U����[�7�}�
$u���_h��y�-d�74u.�D�����H��l�m�b;�^����7��!
h�6��B���;���`�d7�:�C�z#{��0x����������A7r(|�{m����j���������{�����9��A�'@�y�����J����n����`&K��x4W%�M	N���5e�7%&�g��1H�4	d-�S�����������R@�<��}'���Ui*����u�:����3��*�6���/����&�k2p(��@�����u���+�M��w<�\�����F�D9����)���7�T���������ff�dE�k��ka�?�=M&�^�<<[e����g�*��n���h@3a����m��_��������j��a�i<D�s����l�'R����T�hJQ@r&�
�foD���B=c���x#��h��A�%�(|����4�U
B�y!H�O�)�� ��U{��W��S����K����s�g�f���������J�@�/�K>cT����,�y�[��(JQ�fkjW����4�
����6���2���&'�����}cY@�,��e�w*�	�u�m�?���N&��LV`�u�����)�3`�x���_?���h2b3���y4\`;�k�,�of�2��4"	�����fK�������q��aAd��	��E`������?�z���/b[!�1����_+���Ut������[�)��w+��	
�T����v^!�9���y/7����^=���d�����zr~2kNH�6	���@�P����z���p����G]k�3���������'�:�^D��<%]Q��p���Qoz)^���AoF���<��.�*h��;��]���%0kY��l�h�4�R�uM�n�<�����M��i��7����9��H���+�d��\��8��Yf���y�d;�� �����Ih��h��NI#����)Z��.X|���x~�����4��e@37�53l+�u�G����m������NrM��[���r9��"g�BU��;�C�B�E2c������Q���������1�,��6������<��r#?�@���+���
��,S�3�\��q�jG���9�U��sU;�\��*��#�-�^Xmfmsy_���VE��a�}o-����V��T��j�mq���_0�E����zy���7��j���o��X���������<�fH8"��&`�)a������ee���R"�a�"X�o�Xujg��g�A�	O�0�I#�6b�77����s�}.�w����[4�C2 S�Q��C�=2w�L�-e�T����� *������-��m)r'5,�R}������CJS����j������X
����������_����#��,��iR���@��wn��F����g�u���5(�w��QuQ��
�X���u
��\��+&���=�������y�q��2�o�u"(����t[I��E������9�������g>�|���XKK�7��0u)��a`�X�� ���)�����UR�'�Yr�����v�N:q��69_�5���������?pW�n��n���e?$*�8��9�1�{}��G�h��T�tf�l���#�����l|KG}�dFI�zu��^���	�j�����-�����C�{#6��{���������4M.���L����*�oA�s��p�A�h,6�b1���J�*�&ji<���1��\O�a5Q���K)@(e���6��V����]!R��]?��cs�yu�7�pv{S~�D���.��$�-M���%[]:����t"�\���r�x�^�RL���t6�{A�Q������m����|v�R��xs��5|>J='�N5�PGI�/t��#D1dC#\`������6!��[��,��Kz���iI0�=&�gi�q3���UfN�ic���,��A���{�����������m
A��8�g���<.���)�������|1���#��d6G������h��?��`��r=�O��T�9g������(�3F��P�2G�Z-��������yr@,P����`J8�a�]�'����=���T�H�q� ��a�
��������u�iDs����f�(����8����4h�VG��>ma�)�/_e�fp���V�����;�U��y�A�������I`%�o�,���Xk��:�lxd<����_
����u���_�f��u�x{��5`�f�o��Q-�`��l^}�1����1���:�X�<t7�� ����Y`*�tYO���<������L2(KH���l�lYau��
��rs�k��,����rse
K����[w�-y�i��
���8�O�C����W�C���7dp	!�� =�
�%���	~������s��$�\C��iY/�T e1��6�9	��O�*)�K�N�)*���3LJ�f#�D���#cpY��'@���|6}�W�Zu�����0V�!`7�k�����Ec������ov���r�6��b�����&�Uv�fF�_|�:
7aU�o���az�My�Kh`�5����sD�jDW�����b(Lc����=�K�o�����d=\��#t>X�i��0%��3�_�h������QZ,c�*�DN2G��1-�a�/���NY�����n�Y���3�Fe���Y��Y�m�"3�{����R�����W^����V��?��������'�!}A�z�x��*�����L��h���y\���V��u<MOR����
�n?���B�j�4��������xj8A��'��e���G��<�NY�iot<~
F���T��q�������k<����l=Rz
?E�W�'��H�5�@���$�|���nMO����L��T$�����C���SB�eH�oR��Eo�?�n	��~��i�M�i$��n��?�Fs�������x���P��O�����|�}�mK$Z��\����~������{�y����\Qq$�����������iI�����m���[�������~���l��X�7�m{G�RP8�k�*+�4B��@`K3��g���W��#2��5�}G���n��kVi�v����cZ0��
�a>v��98�����_�g��������w��fO{jM�s������O��HC�J�Q���I2'���Cx�_5��k�-d���P��y��z��w��
��<~
K�`!?�P�G�Mv`�9�74�&��]h�|��x���U�Ov?$Q�Xe-�X�
�?X�	�?�����7jE�Q���7�q�X�����A��|��>��HX��cVm�6]
�M<`��i^���c��;9;^��yCN�����EG-�m\�q4������r�0��*7P1�-|���L��O���(��){j�-��5��Z1��OY!��"�j� {-��hR�(�����#8��!�.Se�F92��q"]����W�A���c=C*�y�/Z
(�I"��"�Q���U���F��Z&��@�g�.��/�f�����i���U^�{�v�0Cq�]=�K�cWGX�Q��Dq������=�yF����]��{���h������G@��z�{u��������!�a:\�3�v_�G�w�����Gb����<�4Q0O%�����j-hSc��qh+�7D�B��������`���~���!jQx����c
�F�D��/_�����tP�GNz�)6<�9�����?�Iw��������Y?�x�s�����y��P�N;;����{4���_�S����6��E�P�� ��C�et4j!�*$p!��e	a�5O���������[�p��������Io�p�kQ���m�	���T4�.��HN� l�n���"�ds�T��i;�ON��j�N Yo��+�R6��kph���5[k��������Jj^�%@Z�\?��d�"0�lsbF���P ����c&�*�,2�Z-,����o"��j�0,W���K�s8d���l���,WH�������(����9E�INg��M>k� seL�Y����f��i����n���m�b>���`p��3�%D.^��Ok��r�=@�����R����������}}:b�������\
�B,��V��^!����zo**��UqZ�#(����_
O������'Z��`�l��.:��c+r�+�����U�V�9
f�J���^��\+S_�.;���wU���Tv#��<Kg`�>
���}a6�8���E������r1R��� MR�����k;�� �	�]��1���>��
�����J��������4�����=1��p�	���Tp'�u4g|���q���q����<�7��ZF�������o���|��
�b5R��r�He*��u���SNU�+�/���_�+�"����Up^j�
^��R�d�R��Yj�8��p~2�t���>�B�>u��O�(%@���lP��@@X*x���_�`E���C��m��w��S�*��q2}�5;�[R+��+l�`����*�1pY����,��=i�28����+"j�P{�(����E�5���"jxZ�TK�G-����	l�����j��M2�(�9	Q���-�.��k��-��L2d����?%x�/���5Q�y&"�_�
��X_*e;�,C�H��TW��@:��MDbg��&.�	
t��&����O���&_���
��gp
�E����a�������Y���K��y
�YZ����p���p�y�bA�	�A��m�b�61�o;yO�;����|r��'^���&����B'��%������l���(L�i-D	V��'�,����F\=���7.�|B0���7�)Cv�?{C~���DWI�bf�!��������m4�yB�2����^��=$D��om4��(\%�V�������6���-�]#�(���^�$���r~q]�����^�|ic�`�^kT�����Sg����'�Z��x�YK����[k'����n.�����*���Y�dP��AA[
�p�{�������t���2>�2}�����4
_���@q%N���v5���ui2#�hA����a6�U�*�2v����rE����%����� ���y�v��a�KR�$��8vl���\�iA-����J���T8��Yz����/������)��5���j��eG���iH�y����m���]T�;��j��L���h�xm��[A�mg����=�����A��@f�S����m��JRE?1�!��`e�NX=	�v���W�Mu�i7C/��|��.��#��L)�9�l���"�l6Sj
9@/��)+���O�V�B~��NA=	�����1��Z.v��f1>ik��������:������wU����%�c�]��G����9:�*p�[W�K�_3���x���{��J�2���AXe� �nc���[���]�_XhJ5���P����dt�98F�IL��� �)��,���L���4��:k�e%,?/�"�RX;>y��QN�����5����M��&��m^�;�����	�#3(L"�}w#h�@G�b����hY�6��JLE6�S���������.�����kq'����.:A�e���Yl�|*���)�������{���s�����+���xE,F���s]�����T���^��\�wJ��jx������e�#���W>�����_�?O���,e����A�����Q��������Uq�T�;�m��^�wn{��=���e����t���\�����/��(�����"�ve��H��
�XJ�����������*�r��$/�1d��L�q�����J��yx��C�#c�0
v��MO�}p]������?Jk���/�>:{�����E�cA��(y�#�������WP������fC�m,��xk3#�!#>d�g�-���Pt���:$0�%����|�%��/�x4�d	��������������l�c�/�g>����=������q�����Q�#��z�L����+����\���eZE�g��TY���v�������E�F���Pl�X%)��R���SZ�Lg���J��
���k������#�_�n3�MO����;�����(,N����������� �i������V�YL����B�<��T?���q��N~k��*�r
8s��l�3�����V��	t��}W��C�w;R������<p[�s��5��������2����{b����ka�n�SWt��N�����_X3M5o�<�aP�o�����;�x�!BIG��8���	pG�=��k�
����N��n���!\�&���I����5[W�-�����������;Y
od�k���Ex���^'�;Y��`m�];#n�[����dG�wO�Hl��
C�G
��`��)c�N������\��HL�]u�C{��llVC��5������?]n�N�Ua�Sj�
p��a��t��u?�@+�x��e[j��O��5��zeA��y���W��������P	Cu��J�|bz��aotY���f�����P0��|�6W�
��$n^�h��f��DLs4s��rs��Z�7�� �����4�Eo��MOav��c���z��iBcc�JLhMn=�K59�0��9�3�����4A��9A��9����{=�9:��}����1�����6RU�H��%Q3�������gO��E6��y���)�7(���z��~����S��������W�{�������C����U�<���8�u��
,w^��$�WF"+Le���!<)�+���9;�HT,�����,�R�rI�����Bm��)�X�7��4;�)��>�S�������h�����g}!T�V��`��C#�8xB��W��W_C���`kGE������M{D�"m�C�o1��,7�=���G�P�"�a��@�z���)�M�|4 f0f3�i�L���u~
�;������>�W�w��izt�z�2�E��hc��
_��4�<N����ln��sg%���G�I�66���������n���Arq�J��,H�fkS5;���@lW���f��Im�O�OZ�y1I|�	���)o��B�w�l���D�����&M��;�/
[M���������wo�?<�:�����������G-,
�"�����U�&���(����)3q�	=�������
� �8��mF|�t������f��������Af �J�EE-��]�A����}U?L���'I?�
5�$!
�.�y�3�������(=�Y��D09�����J�G������9�G���%�ki#p5��^$�����|����������{��7�_��>��������{w����>����g�Q}�?o^�>��_�|a~�45��/����m���oy���OoW�n��Qo�oGo�o���}���7����m�����uh����J��w���[a����
m��������S�*��a��
�
@fs���7�w-2�x�I�-�PdF1'������fC�RC�*@��	Q?[������f��q�VZ�����N,���?��i�����d��v���_����F��;�/>P�M��O���i4��a�#?��H���`[U%���v3�7%�y������G�v_~������	��^�Fl��o�H�m�Wd�������A�q�0����^�F���t@f7���`lq��(��a$�0�At���7�*��}`xG���Fxx�*��I��v����>����g�9��$�Y��C`~�%��G�mL���L�����#��OWZ�A���2�N�����Q�Q�
�$#���!^��?���������E���������a���R���������Y��_��~ P��4c�����go��MKb��(I��ef������$&��fr��h�9sp�y��a�������%���gr��������)�m�����e4���wM2Vx<Z�M5j���h6��C�
��.'i�������Q��V-����d�K��U�EK"|��[m)�l�
���0Lb��i�b��
}��]u+4'��T���2�J3�\6sR;�S�b�a�����buw960�S�����)� �~��zC�:A�	��A�D�gO��S�{�'m�h�!A�|��������������)��jEdO]��
n�o�Oc�~��7���u��������J����grr�_�@�7R`y��AZx+�<{N:X�%�v�(Y�����N.�y�+Y'�[�+M���c4��1\F�O*-#��������m�M�� �)�/�dz�yR���'3��C��S=2��r&`M����N� ���'��%o����t����3��)?y������������A2L��!�_"����=���
D*X�!%�`Hd�8&7�t���V�u������ME��&w+5B{r�(��~a�k~�JcQ�$:��H7�@�G$��b>�R�:�T�
�� �����Jm�h�O�E��4*��c�rvJZ$:J�=�����O��<�S�>SE�������+.�|<Gk�m����H�ns����c���,^po�)�(:�KS
a�!�>�	�E�Lv}[�d����*3�������8��O�������nn#��nP�����E?��d�����Ml};6�����������:(�[�@��&� �MsGd�p-���f�z�������G�5�O=1��Fx��.:��~����K��b��8l��zcM|DD^��,������F���m�%�NU
#�>���7_���#��w6�M:��G���B����D��JG������d<����m������t8�ut�B�*z)_�G����k���%����P��s�����p��O�+��	�'����	�F����y�2��'L���Lx������#��hkE��'�b���Hb
��(�_2�gV��`;W<����o�>�����t��G��kZG������v��'��N8]��(��=���z�t�����P���M���L�v2��$���w����%��;��`>/{r����<I��$�{F��P��W�����z3J�'�7<O5�{�^�,0�����p.��z32�	~7�����o�(�(�
�����lU�
Z�Wo��;���|�5&/��{l��d](xfI�E���svJ��H����X�����L��_���O����vd���.��a�r\�x�t���F3�oy�U�D\�)q��A���k��e_}�N6G��0o>T(n)�9�"�*����������4���D���L��
� /��������~'���-��7;���$#������0=��kqPz����@�����QkG���]+x�gg��.�VvB������t�Y;�^�l9�� (�#��Bz[�D21o���`v>������Z�o��,1�5B�O-����Z.*����<�3���Eq^"N�o�F^���������.� ���dv
&+�x���X������x��na��[E��9�����:�#0�����B���b��E1p�+�t.:�ACkAXi�o��+�*Dg�T��D����'��t���A���7���F�
�
���`����n��6o���Zhz4�6D(���l�=*Q�1;�i.YB�M�G�A������UC��������g>�mp~vv�:���J���	�����M<>N�
N�u����m�$��"����D�w�����z�r�����O��b<y�s��g���2iH�����y�e�������L0���ZUF��X�MW*��C��@g��y_
�����y�{F�DA�;��(XM��u�7|�])�xm���"{����<��+�"�V(>�z����A�~#��O�����Xc�	�{�Y�<����6�,�f�	�jw���@����mgv�*�����k�e
>s1m3 �/���GV���L������x�;��_�.'��)���U���46��2�-�	0a������L#���<!��p(8V������� ��M�u��bW�i� 3#>Xo����4��;��?����n����]h�����f�Kes���V7p�a7A����#���If!`D4��	S�(�uM�����fr/�����0��L��N��+5�B+��4E4�$om��H�y3�D:��O����0�������V6G4����>/���X�M��`t�b]��}�	k*p7�~�|*��L]�WE������i��������'%v	�R��0��
��+��,�������n�M�����pOY��%����b���K�ffdg�H��*��P����������^���k4
��,�tb�&���:��7�K����\�>d���hH�Pj)�A*��4��|<y6�������4�1=���9�����E+�4������r
��&���hr������A��d����OZJe��M}�h�&
��i��
uK�LMR:1��kQN
�<�):�Y���P�S����5���Kj��%(B����,�)$8>�v,��pa7�_d����"��Y�x-�S7Q�6C�
�8�}����Rz�9D��O�VLL�S���pvW����9v�3�����e�VY�5V�P�F�rYl������A�O�������� �����^�)!����Qp�A�\�Z��.h���k�<:���[������B�K�P.]L ��C���������U*�/�8R�H�@��
�.�X��Gq���������*��(
�*�tP����#��D�����AAb�RH�)�<�[�E%���9���h|T\��?��s��S(���(���c�O�B���BZ�P)���%4Kq����9��jY�8�h��J3��$.�#�u�&��1-�uE���/GGgj��b%��(�72!�F�"y�eE�����d��C�r ��c9*��]a��H������b$!�����e
rlK������z��
w@�.?���8�����=_�zBYj�t��Z
�|�G�o]�G`�r@����"��3VCC{��1����<���R�x|b��g�E�w��]x~N����c�����T^�1�(����D���$�lY�/����t�=����N���3ee��f��'�l�Y3z����Db�����|����]��L)�������*<W%�#���G����({��>�w�� #�3W9c��;�8v��Vp��C��l��H.�<o��-f��E(vQ+|�/������f���9��B���D�#����9�������C.|�$
_�+�`?��>�*n[��V�����9�J|W�2��e�hzv���N��#�bf�B���$�[��
����X�zT�
��>�%E@���������YR��0�~�]~]�3H��'K-�����F�>j�a-1�F����9�Y�Q�������,/\�OH!�u��SK�W��d�G���W,�wl8ZO8
o�>��Hf�d P]��z����z�8Y�^����z�?2��q�����Y���*`92��+�^{mu)6o����l�J���==��y����["��-��g����U`���v:���?�9��R��x51���1*(�|��t�|C6#�m<t�A���H�#N�������m�H���_�G9�����;���M[w7w�i�$���x�>�DYjdQ)������� |��9���I�`0�!���@��r�NQ	y�Z��e�o(k $'����`�{���O�p/$Sd!���6��J�'�B�Vt��bN���#�L�G��a�����	�~�����fwa�����]�h��["U	r	*7f����	&�\W�����>�.#�Si���R\TpEp������"*@������B�����8��k����w����Qo��<�3e����N$�N���
��<@�-8��H[���c���gZe�5����L������AEA����!���T��O��P��A#p*���Hb�F2S�+�����@KE�Y?�[�'p�Z@a5�:���Q�D.����qsn�,��U����e;��w`1���vW��\��\��#H�j��]U�w�x3��B�����1a�nT����>�W4�77	���-7�c�)n�1�o�<����9��%v
/n�����{3�
)���
Z��!!L|�P���KZ����,��~b/�$E�d��x�KHo�{���Oz������l�Z����B���}�XC�!��YL�z�
x�BkG+]�P�5,��SuG �m��ZM��kH��Y�W�G���r��_�]Z[�X�x���������_G*�9�4���YC�"{_b�"�v����Re�7�L���!��by�odi?��A��L�u�V���$x�P�\w��~���E��*������
�%�	�&>m�<�`)*2�T"I��.g�jI�R.g�|��}X�Gl����CG�*T\�8xv
����Na[p�}�����!�g���5�`�H����n���[�/������,�^������u�e���rnr���1,�c1NCIU}�����eD��dW�y>&�7��
��A^����������5y,����L�dj���-��`U��������T�?��Sj_��.J~�n�H3o����>e0�q�S�m�
t���2*��3��9Mo�J�n"��o'����T��M�k��2����)���i���o���ZD�
��<��3A��Y7@DFsy*5��J������2^M��T�9?]��"/��$��k$)��\FN�3��N��d-&�4�Z"K�|!MuY
�'#!���pg[#i\jJ>5bI���&t���4Zx	w~f����\�x�(�5f05-[[�D��%g��3TG
�'�B�F~Hi����V�"�
�7����)��J�IE$����Q��������?��h�?��/y�:8(�V�^!b�v�8������b�~�k\T&yV�RY�r�������p=
H+giJ�h�b6���h.F,r�+o���U��%,9gn1�$7���IO��!N��&%���e
��S��$��<:�[��J�x���o<!a�2j�	���f���Y
:�`���Ld�I��Z�"Ci���S��d#���]��d����H�_�{��c0�#�b�(^�4�Q&j�v"��Ko~�>�UcW�bTj|o��N"�LG�������P�
mA�eF�H9<�����^���m:]��C�9lkje��\�/�����U�U��c ��N�VUz@�rJ�[Y���g�iu��:K��z)���p�g�����P�����xs��Pe�G<��X@N1��l�������!g�����|���}�Yt�,�x��	��E.�9��g,����	;���l��s�=�R�����z��R�t�|���={1�
Gr|������9$k��mAmI�,�|D�o�B�0�PS�E���O��"��
Nu�|�zM8�
'f�l)W�o��<��VY��)����+FY��BO�Q��i�+�jaX�����c+���[���TzO���Mbn�JP{���S��%�7F$�5�tBk`�zM����s$w6�����2���V\��j�������
���'~YE�z[�(px�%�G(6_�N�hj"��3��,�>4�Er0�!��'f�]NBT��m��-@����
���s^�i�I�;Z������Q�0o\0j(C����g���c�O��lMl�H����Y���57��7E���c��W��U?��������W��g�;�dcohr3�34)��E��@�	��Sqa-����4�%�tq	��q�W�����2^WU����.��"�h�DDb���^Z���K�
�:�q��"[�v��������n�"����!�����d�F��Ff����>�S<s��M�3	����l"o�������������D�6�1^�;E�v�h�<�7y����<�mxLFf��E��������a���w�������rJ0���`����u�Z���WXva�����5��������y|�����`0�TZ�����i
Ll(����$����8U�,,�m��_������Bn�q�4 ��t�N���^�V�s?}8�h`�����S/)���F��n=��t�������n��8���E�n����4���v,6��=�z��iw�iO=�u�w?���lt�N#��e�<Y�Yx�������2
1�i�a�b�^����&p�Q��#���]����Co5v^$��/���f�\�����%?�WF��R������f��yU�7�x�Z�/>\|d��]��ga�6Zz�E�d:��!��U��������D�MMP� ����95{�}�f���
j��_w\����.��c��b��/�U� ���f��h�C �����F�?���0��������kb�\�D}��%�1��.AI��Y@�`:�r��;_Fkd�&�j�������z�d3��8o��@-����%z/y��5L�l�'���>e��O��u�p���Ln����s��e���x����+���s�����@��w���x�\�A���j	��n��h��#�( �ko���Tl�D+��s���3���L��AIn����������f����$1,�����w��w�
�_��Y~:E�_��ie�n�)~�/c���g���c�
_�V����a;�����P�P�����������Qz���/;��Q�-�4�]�j�����n�>�r����	m��=���%��s���������S���qx�q��T�T'����?������Z6|u��*|�=���5TX�R�?�k?6:��5�x��CL������ GL�J�u���x��4�q�{���g���E�.��u�H��-B�����dJ1B���6��-pS��1o���Kg�����H�V����5O�k���8x2'=��r�3r"�0qznD�
������Qa����c����K���-�����t�7�H�l��,�g�X8��0�>�-0[���e���-���Vf���<g(5���)�v��I,v�;��_��#�1���jv��F�uQ}���Y���]�������z�x�|�T�]2+�0�+�AM�����o>^��o����T=2��Lq�U�����.�,���kZo�}�x�����$����2�(����zO���o�n�������q���O��J�{���B������:zZ��@oWo%���e�Wv��xg��&c�u��F�2{1���pw���)}	u��?YN>�c,�R�>i��u��.����A��2jZ��Q�|�����|�u�]R(���L U�;��-&�[���
��@��V�g����
/��M ��y��t~�/���QYf���dz~�fX�
,Go�10X*9+b�����u�a���H���6�C������l����}�=�����H51"��������p�J�U��K�����	��K�E����-��B��4'^�`��q��!����p��8>��?!�G&3\TY��F���,��;�����Q��X���?������A4��2�_�V��>��	��_FX��1���)�N*W@����������������	@�5m��f(J�m����g@�L��NqBKZ,�����y����uZ�1tD�6�>S;f�%����i)yy��� ��j������kP#�a��F�����>�.i	g�(��3t��/�|@&d]=�����0�j���
S�6L�:G���"��Uj�����w�w��K�g��Bb�6��kcj�6��kcj�6���1�]���2Lm�aj�S�e�:A��hk��Mv}#��oh���
���!B��r����K
���K
��(Tq��,���+q����q%9�)<,l�R5���'>����O,r�������.]�~lAy��6B���c��{<8=O�\U�5�E�|����U�Y�"��vQ��.�������o�e�����]vl�����v�1�]zl�7
,R"c�q�]u l'F�yMZ�y]*�y]�y]
�y]��y]��y)m����~^BW?/�������K(�qJ�E��������������t���T��r�y���~~^F=?/����Q���������O"\^����8oX6��J�A��H����S1_�b�^�|v���UkjW�j�j�-cY=%����7��x~s�K���4����.���#H�m�}W^��UF �����g&�J�N��<(�X%(c%	���������O9pS�hT|n9FS�UF_1%�;9��9�v�f�M�8Xld������	&�&L�]F�"5��4-��4�������y����Z�
j�6����~�m:5 ��pM7�>a��a��>�Y���z[�q��O!�?��d�d��*��t�143$��4Q�������0D�����vN���0��SyZ�Qt2d�u�u
�����"����.�dAE�^���3Ts�eg
���1�'���rX�&���F�Y�?6\eN����|�J��'��[:�
(�^pM7h��X���-��(�Q����b���+�
�1�M�`	#KR2l�������������v����:%���zr���w��o�q�y��>�4������q��!���T4�������.����Gw&U|�'Q���3���~����s�7��?J����h�O8Y������B��I�sud|d�(�A�+��Qv�G���~���c���X��!�7�H��������Pm7D\�	b�Sp�}��Js��)6��������������x�
�l�pCx����$�6Y��!X����� �PU/�.&#r�zer��uk>Ufh>�Q�O��Q�waU���OU����~Bf��<�=�Vg�
���%*��f�n��"n���������[����t�g=����zx���
h>�Wzc�s4�j^����|���2�_�"�rtk8L�L.C�aCe)3K�VYls��_J!�wn����J7K-����6���q��;s��Y>/�������X&��1��]��7��10G�Rzi/�n<\/�#I:�.R:������E^oo���o�z	Wva��0#���x?�x�O��sG����,���w������t�a�?����s�
�t{�g�@u��
 ����|�sr�
���������6]$�������k��u�%�
]+c���A���'��^�����(�Sh���A��V���bub�,�}Z�����B�������X��y�\q���$��rj��-@EhNHe�����&dd��Y���f���}Yu����Yy�I	Z�Tb����N��+��!��l��qfnC��$���uR�%
n�eJ;#i?+-�~'�W)5's+'�NOog%����U�vf������3��2����+�Z�=����V^��:�����nhe�C+�#Zy=�Qc;�'����3{���	;�'�����z���	;�'�����{�������y8���;KU���Qr3^^:4|+�_2v��A?������s.|��y��%`;IZ?�c��x�e����K?�]$G�:W}��<O��PS_�Ai3�b` �������KNv^
`J�����^K�6���x����z���13����ez,
��c)��KL��q�K��=_��R�s(��Nxhi3:_�{9��F��(�)�g!X��^��0
�l7R��e�]: ��7���S6��Y*�X��A�4�����)Z�$�!�
D
L����"���c���b����!)�r2��:���HOk�(\�XK��'j���Eu���W>��*�1��~��w(��H��scE��&��GX�����n��Y��:��K�y�l�j�^U�6�yN��(c���ql|��Itq���������p�,/���3c��z�[���"�n�J�9� ��S!oZuB��_#o��/����(�4<�Aj��m���A��n����Ph�����R��������+��O��;��"wy{������Y:g4��d���m5s�������g��e'(/�8���36r�����d�a�.�{c���}����l�L�>�2
&�9sF��'�%4���{���7K�����)[�.�]!g9�Kg9�= >&-^7g>��Q��LTs���#�����������?t���[�_
'7r�}�E�[g���m�����r��f�����{��N���b�0�vj� ���j	M�f������!��BND�#�S���>��B��"�X�����@D*����Kx@��:s������z��.��>'{�[�,���^�bC��3g���a��������U`!B�^-��FC�.��p(C}u���B��S��-�����fS?���Y~����J+{4}6v����D��-�<����*x�n��Q�(*��s|z;#�W�������#)4@>_�����=���V�3�>��y������)��i��n�Q �4�W��������W4g6��`�������������g����g�
9�*�Og�4s����Lo�)yx�_��3�y_��q�qE��9v���	��:a���$i�T��1�;ST�N�g����D��
r�	��~�a��9�V�0lt-������c�wE����sCd�o9���O$	?���5����	+�n�l���%�0J����1�H��>���~�VK6�Mq�9��l:$��F$?%�i2��a�=�">�3�,^���0��{m���qp��i�L��D�>��>��1��Nl�t�2 ��U�@!"5������\sK�G\}���g�BX�P��~s��/�_�$z#,��|��S�(��u�6���pE	
��d�Y��T��>����Q� �����~�.�I'1/5s(�����|^QE=j��+d�L��>���e=
��S
L��	lAK�$��5����&����(#o��?�<�z�>z�I��X��(1��u�����d0���a1��sN��k���9��[�LI�3����k�����x,���\��O��"(��c��/>�9g�[g��$G��8
}��1���'�XG`kA>�i0�kO�����;����zr�+��D���`��F>���y	��������B��f�������,=�f�aE��n6&S�������P��6�o�*�X�x-C�EY!���k�=�����_�{M>@9�o���j�+��0��
{	2�)(RC���
�^A��~'�� gb`C��w�����'!�?�``�������4��H a=�(��.?YwWjW\Bc-"@�:Mfdw���G���Rag|e�_�C�o�e��`�l|��w�+�j����X����ft������Qlv�2�!�27�d�@9���
�[(�RH
�N�s�����uL�![��rK��(��"�"d�e��so�O}�?1j?���a��������P�P}���i��
�
.�`�����{�l��
�Xi�3��M����C�R`��+`;���]��`�te*?>3F��H�b�8�������1&���462�K��!D��Q������{H&�$���"
 	!�4����h����4�X����
�o8���s2�?��O��]�<��Z���8C�BF�7'=��t�m��P����z-G����["m��*�es��f����>]~�-�i�-UE��#�+����5Y*
�)��(*
�cI�-������]8�	�E�	E{e5��:����q�F�p��$L��&�_ p��l�����e:�q�%��&3
4�El�����<c�!m	��>���k�9�!�8�Q\��r�l!Ji]��A�%�`(1@�oW�Ch;<�sr����[h=,�-�]�p�c;���aI<�+�������0m��KJ>u�S�b��SSd�K�%���ZL�7�@������t)|���o@�&�"g��Y��Jp���+�D�T{����xo���p����7�����=	ng���������;��N�(����ldF>M 5pnK�iKk�	�^�<�5{�V/fY���Sq'A����
d�pZ�
]�BFI��w/�.���}�C�#�o�r�����n�|�9��G/�go�p��y�8�x�@"�%]�4���R�eW)�HQ��j���]IW��i����������`"��Y|�G����	�4���1�%��4C�0
������_�4�%�gQw+ia7�m������x�^V����e��n-����jZ<=��������_<M�_�����LiG(O������'F�9M40Q^
�+�����w���7�k|���@�q=����C�w�siL�cFi�T������
��?���}������`�~�a��K?�����Ik �����WR��7�"�tAimD�������:��d�
����3�	�\(��Lnl��$�;���a9���l��AO�������� <Qv=E	��f������(������E��K���}�q��fxw#�Fm�rdi�{���J���W1�x�}p��$��q f����t�/��@�Ti|Du��
X�q�L���������B$m���c|�y-�_44����M���.x��a5"�}����W8������8��������;Yz_�3;�������� L~����u�z��a��.���B�[RR��>0^�������:=h������
��CV���
���\�Ou���a��n\�AF�;�v'��N����j7S�M,n���,><D'���?��C�a�O=�����h�@>q�R9R�2H����vnq��RHJ-��7p~����c Q
"@�
,�)��R��wy�[^��@EYT�\)�r���i-����`������sA��H�B,\����h�����8��%H�����3t�BeQy��v�3�/��b�,����#���%�:�#h]����]���R��o�%���:]$$P�7B��I����s��Gj�����G������t�+����xU��9����0�z�	1��UE�����[O��t��$w>�������<���(1���v�:�7#�����!����V]�T��mN`<4S��
@�7b7���������y#B�����T���}��e��n�76#D��/D*�rMS�����P��������C0�%��)N~��U9��b�7�7�i(:K�]���
X9��>�_��y������a��5��l)%<6~oX����!Y������6�*���_#���s�^e��vQo�e.N��$�("����9�=0���ajq7!Ix�m(^��e$^>�D��+�����S]=p���S/h��'�p36V��`'(Q?��Y�L�S%E%�y�	0W��$�2��Kb0���Ac3�����Ru=r�i-�V�b�������kA����C��vQ����uf3:�2O��;������E�7@NHLH�2���$�]���j������t�?�#�����$�H=���GH��~�>R�E���M���=s��p�n>
I���L��dH��#�����/��)��(|pH���LV��7���_j���L_~����������_��&3&T&T���#�k�'��9���!���jFUb
��]I@-^&������Q��IG��%��� h*���R5UW:�`�i�C�����-���F86��]�t��������V���4�_�c�~o3������������;��b�9�Zg��G���5��Q������	�����/t�����C?[�e�����|���� Kt<��}Z��C$)\#��1Plg#��/\���/
f�]�����nxC������U�x8";V��ey�+4�o��W��P^�O����)/���IO':c7���������t�V^<:m�P���������D����q���G�[�o�3�3D�\�>�����O�#�p�����Nh����v�g�N�����m��z�����#�,��������R���
�t�2��#D����<�<tf3WI�
�����zD�tq=�w^f�Mk��l���s�Lg�0��Fx
wH�1��"t�!$X���x�����KF��`:t��i:1=)�,����^BT�=s:��'�]��;�pS���
�����a����/~V9)���|kT�[���p����/H\������FZ?6��]�R����a�t�Q���J����!����'�<]�cDv>�f����v	����s��qs�.�0$a��#	O�,I�j�I��&S2�+����%�	���!�*L9\\H����B��p�G�e�d/L.���K
���	��
�(����r�KCl��jH���!�/���P�����Kb������iLA�Mf�)�����l�l��FtH�����u�	��)kg�"
��"G:-��c��ve����XjQh���Zo�D�
P�{d�Wp�����J�Zi�����#V�O�[�8���*�������G�Q��	� �z��C
�]�:��T+�B�Hx������;��dj^�{
��*A�K�u���t�GV���������c�������I��.B�W 3U���R
��S��M_*�d����'��`�����A%��y#�������r�G���ws�������;�e�R�r�{��YB����"���jE�h��r[_+��E\P���\�*.z#��8+�h����@��*�2�Q����H�R���15�'J��$,LxB�f�Xc��
�0�8f|�a�O�l8&�[tN����&�����)�
��v�o�6���~���PU���@�����I=���ZU�k�xc��J=s�?�]1������L�<�V��"�Y:8935r/C���tH������_y �����%G��a��|����b5Q~�0	�7���wg.��Wzb1��sC�f
Z�����Vd�sz����
��[�,��������S1C���ST9���u%�~�������U�k9#>P4�r�(������H�G�1q������*5�?9�<���^������������$��'����(XN����!j]��Il��l~ye�j�wCZ���;cA��"n���I6.&��=~���oX��������.�)5�;�K��vz/��K�^�z�_>�F:T�=%��z0�z�:��6�����F(��A��UM�t~j����slH��X��P�y(V���~�z�Z�`��9P��d�b5����3v�OA��e��n�1���8Nbk%d'i%@�����S�2�oe!�+�Tg���u-V���&s���^F�Q��-�S�t*b�x �B�w�J;�K����r!� ���n2�wSR���|u&��X�u������#N��3����G�0�$��������6T:��D1z�
���zh�kgo��d�{�{�=p�\�fl���K��7��h������r5�)���\z�����0���k+�X�&G`A����
�z��K��x+�v�>����P"j�'��f��z�Oh�c�$�n�I�`�y�fV^5,B�ZMA/5.�F���|���=�����o>^������6����O�`���8��������������d�ur,��h�c?�b��\�������+�tYE������x���\_��_?NBw��):��B6��d��G� ��%M[�g*|.��y_�X���Lg�{�i�^�E�.\B� �)ri�3F��8&��a���8M�>%F���H�����1g'�u$�8��68F��b���8�#|��7:�A&d�Yz�����7�0���'��#/������O�����Sc���I�?�:�*s�|���P��eJ��@�����;���L��c�u2�-�����
��.�N�5z
T��������F�Z���eHpO��qq#�����C(������wc����������oERP�k$����t����W
%�������	�%;������Vey.;��_�����C;������#T:�^�H�C6���b��fo�Zq��OHCM+P�1"�eH�����}�t�����9���j�1_���%V�*w3���c�y~�������eLM��2�Zez4�
$R��Py��	�E	�����M�B�G����8(+��2h�����Q���xB����!��RbG����
�Cp���Q���1<a��
�7m���":�Mt�x>g�S�������L�S2��0���6��?����������/�p��B��1�����n��B�����j�t0��n�qp-X��z�eZ7$,/�Vq+�����+!�}�3%�p"�s��6�����l4Vd����*B�P�����]��V�����oVt������wT����8$��-<�aJy��U���E�����f�B��[�
	mAR�x�_EWb#yZ[�#E�?����Z-���a�~/R�u��)<����e����O�`�*s6��2^i�1>�
D/����!��C������h��B���+��.�����Av��8;����*�x�?��(a)"If��8f����Vx�_�������/��(f=�@����H������o�7�+��R�/�����p�_��j<fo>��)2��&L�jp������B�%�L���	�B�Z��?��^YG��u����zOO����m�)��>��o��$�2��4D����r����.�K�P��]=��������@����c�:��������w9�i�C������� u��#�Y)e�v~|=�������{��fU��,�����"��Gg)|������1E��Hn+��j��@�z���8��+�����fc]sl
LV��������O�k9n?=s��@�z�K1!F\�H�S�
��W7���Iu��V>#��b}�}�_K��|��;G&�#Z�Q�E+,$|�~�g�{1�
P/������8��('� }0����r�"N�P���f�G"<�5?-�Z��A�����q����8����p�@an?��sGz���Kn��gHtyyn��qCB%���:Z�.� �l��0�NN���WvD�z@�2��j��q�k���`�&[D#o�Ip�w��s8�@j���]��c:2$��� �Q�x<��u�������
����pO�bi��YL�60��=vt���������x��u���}?i�Z�V*V��uD����o6�����y�s����f���|����z��/������w�o�����dL��/�0����g�����_�������_��������� �I����.P�������d�w������k�r���<X���3�������g@����yZ��>�-��!�x�%R���
�5N��>�U��(�}6������~�$��P�6d��*��?�mmm1�V������Bk'��d���;[�GB
#25Erik Rijkers
er@xs4all.nl
In reply to: Oleg Bartunov (#24)
1 attachment(s)
Re: nested hstore patch (sgml typo)

On Wed, January 8, 2014 22:29, Oleg Bartunov wrote:

Attached is a new version of patch, which addresses most issues raised
by Andres.

[ nested_hstore-0.42.patch.gz ]

Building documentation fails:

openjade:hstore.sgml:1010:18:E: end tag for element "A" which is not open
openjade:hstore.sgml:1011:13:E: document type does not allow element "TYPE" here
openjade:hstore.sgml:1012:8:E: document type does not allow element "TYPE" here
openjade:hstore.sgml:1012:27:E: document type does not allow element "TYPE" here
openjade:hstore.sgml:1013:15:E: document type does not allow element "PROGRAMLISTING" here
openjade:hstore.sgml:1024:8:E: end tag for "TYPE" omitted, but OMITTAG NO was specified
openjade:hstore.sgml:1010:3: start tag was here
make: *** [HTML.index] Error 1
make: *** Deleting file `HTML.index'

This is caused by a small tag typo.

The attached fixes that hstore.sgml typo.

thanks,

Erikjan

Attachments:

hstore.sgml.difftext/x-patch; name=hstore.sgml.diffDownload
--- doc/src/sgml/hstore.sgml.orig	2014-01-08 23:32:29.493548857 +0100
+++ doc/src/sgml/hstore.sgml	2014-01-08 23:33:02.554527949 +0100
@@ -1007,7 +1007,7 @@
 
   <para>
    But <literal>populate_record()</> supports more complicated records and nested
-   <type>hstore</a> values, as well. It makes an effort to convert
+   <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>
#26Jim Nasby
jim@nasby.net
In reply to: Pavel Stehule (#22)
Re: nested hstore patch

On 12/23/13, 9:47 AM, Pavel Stehule wrote:

Has anybody looked into how hard it would be to add "method" notation
to postgreSQL, so that instead of calling

getString(hstorevalue, n)

we could use

hstorevalue.getString(n)

yes, I played with it some years ago. I ended early, there was a problem with parser - when I tried append a new rule. And because there was not simple solution, I didn't continue.

But it can be nice feature - minimally for plpgsql coders.

Isn't there also some major problem with differentiating between schema/table/field with that too? I recall discussion along those lines, though maybe it was for the idea of recursive schemas.
--
Jim C. Nasby, Data Architect jim@nasby.net
512.569.9461 (cell) http://jim.nasby.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Andrew Dunstan
andrew@dunslane.net
In reply to: Oleg Bartunov (#24)
Re: nested hstore patch

On 01/08/2014 04:29 PM, Oleg Bartunov wrote:

Attached is a new version of patch, which addresses most issues raised
by Andres.

It's long holidays in Russia now and it happened that Teodor is
traveling with family, so Teodor asked me to reply. Comments in code
will be added asap.

Oleg,

Please merge in the jsonb work and resubmit. See
<https://github.com/feodor/postgres/commits/jsonb_and_hstore&gt; I not that
this repo does not apparently contain any of your latest changes.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28Josh Berkus
josh@agliodbs.com
In reply to: Teodor Sigaev (#1)
Re: nested hstore patch

On 01/09/2014 06:12 AM, Andrew Dunstan wrote:

Oleg,

Please merge in the jsonb work and resubmit. See
<https://github.com/feodor/postgres/commits/jsonb_and_hstore&gt; I note that
this repo does not apparently contain any of your latest changes.

I'll go further and say that if the Hstore2 patch doesn't support JSONB
for 9.4, we should postpone it to 9.5. We really don't want to get into
a situation where we need an Hstore3 because we accepted an Hstore2
which needs to be rev'd for JSON.

Especially since there's no good reason for the JSON changes not to be
merged already.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Andrew Dunstan
andrew@dunslane.net
In reply to: Josh Berkus (#28)
1 attachment(s)
Re: nested hstore patch

On 01/09/2014 02:11 PM, Josh Berkus wrote:

On 01/09/2014 06:12 AM, Andrew Dunstan wrote:

Oleg,

Please merge in the jsonb work and resubmit. See
<https://github.com/feodor/postgres/commits/jsonb_and_hstore&gt; I note that
this repo does not apparently contain any of your latest changes.

I'll go further and say that if the Hstore2 patch doesn't support JSONB
for 9.4, we should postpone it to 9.5. We really don't want to get into
a situation where we need an Hstore3 because we accepted an Hstore2
which needs to be rev'd for JSON.

Especially since there's no good reason for the JSON changes not to be
merged already.

After some work by Oleg, for which I'm grateful, and a little more by
me, here is a combined patch for the jsonb and nested hstore work.

Outstanding issues with the jsonb stuff:

* I have replicated all the json processing functions for jsonb
(although not the json generating functions, such as to_json). Most
of these currently work by turning the jsonb back into json and then
processing as before. I am sorting out some technical issues and
hope to have all of these rewritten to use the native jsonb API in a
few days time.
* We still need to document jsonb. That too I hope will be done quite
shortly.
* The jsonb regression test currently contains U+ABCD - I guess we'd
better use some hex encoding or whatever for that - unlike json, the
jsonb de-serializer dissolves unicode escapes.

cheers

andrew

Attachments:

nested_hstore_and_jsonb.patchtext/x-patch; name=nested_hstore_and_jsonb.patchDownload
diff --git a/contrib/hstore/.gitignore b/contrib/hstore/.gitignore
index 5dcb3ff..b84aac6 100644
--- a/contrib/hstore/.gitignore
+++ b/contrib/hstore/.gitignore
@@ -2,3 +2,6 @@
 /log/
 /results/
 /tmp_check/
+/hstore_gram.c
+/hstore_gram.h
+/hstore_scan.c
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 43b7e5f..fb9421f 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -2,13 +2,16 @@
 
 MODULE_big = hstore
 OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
-	crc32.o
+		hstore_gram.o
 
 EXTENSION = hstore
-DATA = hstore--1.2.sql hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
-	hstore--unpackaged--1.0.sql
+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
 
-REGRESS = hstore
+REGRESS = hstore nested types
+
+EXTRA_CLEAN = y.tab.c y.tab.h \
+				hstore_gram.c hstore_scan.c hstore_gram.h
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
@@ -20,3 +23,13 @@ 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
deleted file mode 100644
index c82fc66..0000000
--- a/contrib/hstore/crc32.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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 <srg@quick.com>
- */
-
-#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
deleted file mode 100644
index f5bfd82..0000000
--- a/contrib/hstore/crc32.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * 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/expected/hstore.out b/contrib/hstore/expected/hstore.out
index 2114143..bf22abc 100644
--- a/contrib/hstore/expected/hstore.out
+++ b/contrib/hstore/expected/hstore.out
@@ -199,6 +199,24 @@ select 'aa=>"NuLl"'::hstore;
  "aa"=>"NuLl"
 (1 row)
 
+select 'aa=>nul'::hstore;
+   hstore    
+-------------
+ "aa"=>"nul"
+(1 row)
+
+select 'aa=>NuL'::hstore;
+   hstore    
+-------------
+ "aa"=>"NuL"
+(1 row)
+
+select 'aa=>"NuL"'::hstore;
+   hstore    
+-------------
+ "aa"=>"NuL"
+(1 row)
+
 select e'\\=a=>q=w'::hstore;
    hstore    
 -------------
@@ -432,63 +450,63 @@ select hstore 'a=>NULL, b=>qq' ?& '{}'::text[];
 
 -- delete
 select delete('a=>1 , b=>2, c=>3'::hstore, 'a');
-       delete       
---------------------
- "b"=>"2", "c"=>"3"
+     delete     
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>null , b=>2, c=>3'::hstore, 'a');
-       delete       
---------------------
- "b"=>"2", "c"=>"3"
+     delete     
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'b');
-       delete       
---------------------
- "a"=>"1", "c"=>"3"
+     delete     
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'c');
-       delete       
---------------------
- "a"=>"1", "b"=>"2"
+     delete     
+----------------
+ "a"=>1, "b"=>2
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'd');
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'a'::text;
-      ?column?      
---------------------
- "b"=>"2", "c"=>"3"
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>null , b=>2, c=>3'::hstore - 'a'::text;
-      ?column?      
---------------------
- "b"=>"2", "c"=>"3"
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'b'::text;
-      ?column?      
---------------------
- "a"=>"1", "c"=>"3"
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'c'::text;
-      ?column?      
---------------------
- "a"=>"1", "b"=>"2"
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'd'::text;
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text)
@@ -500,21 +518,21 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text)
 
 -- delete (array)
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','e']);
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','b']);
-       delete       
---------------------
- "a"=>"1", "c"=>"3"
+     delete     
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['a','c']);
-  delete  
-----------
- "b"=>"2"
+ delete 
+--------
+ "b"=>2
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]);
@@ -524,27 +542,27 @@ select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]);
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, '{}'::text[]);
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','e'];
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','b'];
-      ?column?      
---------------------
- "a"=>"1", "c"=>"3"
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c'];
  ?column? 
 ----------
- "b"=>"2"
+ "b"=>2
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']];
@@ -554,9 +572,9 @@ select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']];
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - '{}'::text[];
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c'])
@@ -575,15 +593,15 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - '{}'::text[])
 
 -- delete (hstore)
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>4, b=>2'::hstore);
-       delete        
----------------------
- "c"=>"3", "aa"=>"1"
+     delete      
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>NULL, c=>3'::hstore);
-       delete        
----------------------
- "b"=>"2", "aa"=>"1"
+     delete      
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore);
@@ -593,27 +611,27 @@ select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore);
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'b=>2'::hstore);
-       delete        
----------------------
- "c"=>"3", "aa"=>"1"
+     delete      
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, ''::hstore);
-            delete             
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+         delete          
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>4, b=>2'::hstore;
-      ?column?       
----------------------
- "c"=>"3", "aa"=>"1"
+    ?column?     
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>NULL, c=>3'::hstore;
-      ?column?       
----------------------
- "b"=>"2", "aa"=>"1"
+    ?column?     
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore;
@@ -623,15 +641,15 @@ select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore;
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore;
-      ?column?       
----------------------
- "c"=>"3", "aa"=>"1"
+    ?column?     
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - ''::hstore;
-           ?column?            
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+        ?column?         
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore)
@@ -650,33 +668,33 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ''::hstore)
 
 -- ||
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f';
-                 ?column?                  
--------------------------------------------
- "b"=>"g", "aa"=>"1", "cq"=>"l", "fg"=>"f"
+               ?column?                
+---------------------------------------
+ "b"=>"g", "aa"=>1, "cq"=>"l", "fg"=>f
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'aq=>l';
-                 ?column?                  
--------------------------------------------
- "b"=>"2", "aa"=>"1", "aq"=>"l", "cq"=>"3"
+              ?column?               
+-------------------------------------
+ "b"=>2, "aa"=>1, "aq"=>"l", "cq"=>3
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'aa=>l';
-            ?column?            
---------------------------------
- "b"=>"2", "aa"=>"l", "cq"=>"3"
+          ?column?          
+----------------------------
+ "b"=>2, "aa"=>"l", "cq"=>3
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || '';
-            ?column?            
---------------------------------
- "b"=>"2", "aa"=>"1", "cq"=>"3"
+         ?column?         
+--------------------------
+ "b"=>2, "aa"=>1, "cq"=>3
 (1 row)
 
 select ''::hstore || 'cq=>l, b=>g, fg=>f';
-            ?column?            
---------------------------------
- "b"=>"g", "cq"=>"l", "fg"=>"f"
+           ?column?           
+------------------------------
+ "b"=>"g", "cq"=>"l", "fg"=>f
 (1 row)
 
 select pg_column_size(''::hstore || ''::hstore) = pg_column_size(''::hstore);
@@ -759,21 +777,21 @@ select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']);
-       slice        
---------------------
- "b"=>"2", "c"=>"3"
+     slice      
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['aa','b']);
-        slice        
----------------------
- "b"=>"2", "aa"=>"1"
+      slice      
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b','aa']);
-             slice             
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+          slice          
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select pg_column_size(slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']))
@@ -907,9 +925,9 @@ select pg_column_size(hstore(ARRAY['a','b','asd'], ARRAY['g','h','i']))
 
 -- records
 select hstore(v) from (values (1, 'foo', 1.2, 3::float8)) v(a,b,c,d);
-                   hstore                   
---------------------------------------------
- "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3"
+                  hstore                  
+------------------------------------------
+ "a"=>"1", "b"=>"foo", "c"=>1.2, "d"=>"3"
 (1 row)
 
 create domain hstestdom1 as integer not null default 0;
@@ -918,9 +936,9 @@ create table testhstore1 (a integer, b text, c numeric, d float8, e hstestdom1);
 insert into testhstore0 values (1, 'foo', 1.2, 3::float8);
 insert into testhstore1 values (1, 'foo', 1.2, 3::float8);
 select hstore(v) from testhstore1 v;
-                        hstore                        
-------------------------------------------------------
- "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3", "e"=>"0"
+                       hstore                       
+----------------------------------------------------
+ "a"=>"1", "b"=>"foo", "c"=>1.2, "d"=>"3", "e"=>"0"
 (1 row)
 
 select hstore(null::testhstore0);
@@ -936,7 +954,7 @@ select hstore(null::testhstore1);
 (1 row)
 
 select pg_column_size(hstore(v))
-         = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore)
+         = pg_column_size('a=>"1", b=>"foo", c=>1.2, d=>"3", e=>"0"'::hstore)
   from testhstore1 v;
  ?column? 
 ----------
@@ -1336,6 +1354,7 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
     42
 (1 row)
 
+RESET enable_seqscan;
 drop index hidx;
 create index hidx on testhstore using gin (h);
 set enable_seqscan=off;
@@ -1375,6 +1394,7 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
     42
 (1 row)
 
+RESET enable_seqscan;
 select count(*) from (select (each(h)).key from testhstore) as wow ;
  count 
 -------
@@ -1437,6 +1457,8 @@ select distinct * from (values (hstore '' || ''),('')) v(h);
 (1 row)
 
 set enable_sort = true;
+RESET enable_hashagg;
+RESET enable_sort;
 -- btree
 drop index hidx;
 create index hidx on testhstore using btree (h);
@@ -1444,7 +1466,7 @@ set enable_seqscan=off;
 select count(*) from testhstore where h #># 'p=>1';
  count 
 -------
-   125
+   884
 (1 row)
 
 select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
@@ -1453,39 +1475,63 @@ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexe
      1
 (1 row)
 
+--gin hash
+drop index hidx;
+create index hidx on testhstore using gin (h gin_hstore_hash_ops);
+set enable_seqscan=off;
+select count(*) from testhstore where h @> 'wait=>NULL';
+ count 
+-------
+     1
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC';
+ count 
+-------
+    15
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+ count 
+-------
+     2
+(1 row)
+
+RESET enable_seqscan;
+drop index hidx;
 -- json
 select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
-                                         hstore_to_json                                          
--------------------------------------------------------------------------------------------------
- {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+                                    hstore_to_json                                     
+---------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
-                                              json                                               
--------------------------------------------------------------------------------------------------
- {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+                                         json                                          
+---------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
-                                   hstore_to_json_loose                                   
-------------------------------------------------------------------------------------------
- {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}
+                                 hstore_to_json_loose                                  
+---------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 create table test_json_agg (f1 text, f2 hstore);
 insert into test_json_agg values ('rec1','"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'),
        ('rec2','"a key" =>2, b => f, c => "null", d=> -12345, e => 012345.6, f=> -1.234, g=> 0.345e-4');
 select json_agg(q) from test_json_agg q;
-                                                          json_agg                                                          
-----------------------------------------------------------------------------------------------------------------------------
- [{"f1":"rec1","f2":{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}},      +
-  {"f1":"rec2","f2":{"b": "f", "c": "null", "d": "-12345", "e": "012345.6", "f": "-1.234", "g": "0.345e-4", "a key": "2"}}]
+                                                       json_agg                                                        
+-----------------------------------------------------------------------------------------------------------------------
+ [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}},           +
+  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.0000345, "a key": 2}}]
 (1 row)
 
 select json_agg(q) from (select f1, hstore_to_json_loose(f2) as f2 from test_json_agg) q;
-                                                       json_agg                                                       
-----------------------------------------------------------------------------------------------------------------------
- [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}},       +
-  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.345e-4, "a key": 2}}]
+                                                       json_agg                                                        
+-----------------------------------------------------------------------------------------------------------------------
+ [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}},           +
+  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.0000345, "a key": 2}}]
 (1 row)
 
diff --git a/contrib/hstore/expected/nested.out b/contrib/hstore/expected/nested.out
new file mode 100644
index 0000000..a7786ce8
--- /dev/null
+++ b/contrib/hstore/expected/nested.out
@@ -0,0 +1,2249 @@
+SELECT 'ff => {a=>12, b=>16}'::hstore;
+          hstore          
+--------------------------
+ "ff"=>{"a"=>12, "b"=>16}
+(1 row)
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123'::hstore;
+               hstore                
+-------------------------------------
+ "ff"=>{"a"=>12, "b"=>16}, "qq"=>123
+(1 row)
+
+SELECT 'aa => {a,aaa}, qq=>{ a=>12, b=>16 , c=> { c1, c2}, d=>{d1=>d1, d2=>d2, d1=>d3} }'::hstore;
+                                             hstore                                             
+------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>12, "b"=>16, "c"=>["c1", "c2"], "d"=>{"d1"=>"d3", "d2"=>"d2"}}
+(1 row)
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+                                               hstore                                               
+----------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>"12", "b"=>"16", "c"=>["c1", "c2"], "d"=>{"d1"=>"d1", "d2"=>"d2"}}
+(1 row)
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+                                                        hstore                                                         
+-----------------------------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>"12", "b"=>"16", "c"=>["c1", "c2", ["c3"], {"c4"=>4}], "d"=>{"d1"=>"d1", "d2"=>"d2"}}
+(1 row)
+
+SELECT 'ff => {a,aaa}'::hstore;
+       hstore       
+--------------------
+ "ff"=>["a", "aaa"]
+(1 row)
+
+select 'null'::hstore;
+ hstore 
+--------
+ NULL
+(1 row)
+
+select '{null}'::hstore;
+ hstore 
+--------
+ [NULL]
+(1 row)
+
+select ''::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+select '{}'::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+--test optional outer braces
+SELECT	'a=>1'::hstore;
+ hstore 
+--------
+ "a"=>1
+(1 row)
+
+SELECT	'{a=>1}'::hstore;
+ hstore 
+--------
+ "a"=>1
+(1 row)
+
+SELECT	'{a,b}'::hstore;
+   hstore   
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT	'{a,{b}}'::hstore;
+    hstore    
+--------------
+ ["a", ["b"]]
+(1 row)
+
+SELECT	'{{a},b}'::hstore;
+    hstore    
+--------------
+ [["a"], "b"]
+(1 row)
+
+SELECT	'{a,{b},{c}}'::hstore;
+       hstore        
+---------------------
+ ["a", ["b"], ["c"]]
+(1 row)
+
+SELECT	'{{a},{b},c}'::hstore;
+       hstore        
+---------------------
+ [["a"], ["b"], "c"]
+(1 row)
+
+SELECT	'{{a},b,{c}}'::hstore;
+       hstore        
+---------------------
+ [["a"], "b", ["c"]]
+(1 row)
+
+SELECT	'{a,{b=>1}}'::hstore;
+     hstore      
+-----------------
+ ["a", {"b"=>1}]
+(1 row)
+
+SELECT	'{{a},{b=>1}}'::hstore;
+      hstore       
+-------------------
+ [["a"], {"b"=>1}]
+(1 row)
+
+SELECT	'a'::hstore;
+ hstore 
+--------
+ "a"
+(1 row)
+
+SELECT	'{a}'::hstore;
+ hstore 
+--------
+ ["a"]
+(1 row)
+
+SELECT	''::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+SELECT	'{}'::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+--nested json
+SELECT	hstore_to_json('a=>1');
+ hstore_to_json 
+----------------
+ {"a": 1}
+(1 row)
+
+SELECT	hstore_to_json('{a=>1}');
+ hstore_to_json 
+----------------
+ {"a": 1}
+(1 row)
+
+SELECT	hstore_to_json('{a,b}');
+ hstore_to_json 
+----------------
+ ["a", "b"]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b}}');
+ hstore_to_json 
+----------------
+ ["a", ["b"]]
+(1 row)
+
+SELECT	hstore_to_json('{{a},b}');
+ hstore_to_json 
+----------------
+ [["a"], "b"]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b},{c}}');
+   hstore_to_json    
+---------------------
+ ["a", ["b"], ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b},c}');
+   hstore_to_json    
+---------------------
+ [["a"], ["b"], "c"]
+(1 row)
+
+SELECT	hstore_to_json('{{a},b,{c}}');
+   hstore_to_json    
+---------------------
+ [["a"], "b", ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b=>1}}');
+ hstore_to_json  
+-----------------
+ ["a", {"b": 1}]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b=>1}}');
+  hstore_to_json   
+-------------------
+ [["a"], {"b": 1}]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b=>1},{c}}');
+      hstore_to_json      
+--------------------------
+ [["a"], {"b": 1}, ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('a');
+ hstore_to_json 
+----------------
+ "a"
+(1 row)
+
+SELECT	hstore_to_json('{a}');
+ hstore_to_json 
+----------------
+ ["a"]
+(1 row)
+
+SELECT	hstore_to_json('');
+ hstore_to_json 
+----------------
+ {}
+(1 row)
+
+SELECT	hstore_to_json('{}');
+ hstore_to_json 
+----------------
+ {}
+(1 row)
+
+SELECT hstore_to_json('"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore);
+                                                     hstore_to_json                                                      
+-------------------------------------------------------------------------------------------------------------------------
+ {"aa": ["a", "aaa"], "qq": {"a": "12", "b": "16", "c": ["c1", "c2", ["c3"], {"c4": 4}], "d": {"d1": "d1", "d2": "d2"}}}
+(1 row)
+
+--
+SELECT 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'ff', 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'qq', 
+	   ('ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'Y') IS NULL AS t, 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'x'; 
+     ?column?     | ?column? | t | ?column? 
+------------------+----------+---+----------
+ "a"=>12, "b"=>16 | 123      | t | [1, 2]
+(1 row)
+
+SELECT '[ a, b, c, d]'::hstore -> 'a';
+ ?column? 
+----------
+ a
+(1 row)
+
+--
+CREATE TABLE testtype (i int, h hstore, a int[], j json);
+INSERT INTO testtype VALUES (1, 'a=>1', '{1,2,3}', '{"x": 2}');
+SELECT populate_record(v, 'i=>2'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""a""=>1","{1,2,3}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, a=>{7,8,9}'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""a""=>1","{7,8,9}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""b""=>3","{7,8,9}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}'::hstore) FROM testtype v;
+                populate_record                
+-----------------------------------------------
+ (2,"""b""=>3","{7,8,9}","{""a"": [1, 2, 3]}")
+(1 row)
+
+--complex delete
+SELECT 'b=>{a,c}'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>1'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT '[2,3,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[a,2,3,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[a,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>1'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT ''::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '{a, 1 , b,2, c,3}'::hstore - ARRAY['d','b'];
+      ?column?       
+---------------------
+ ["a", 1, 2, "c", 3]
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v'::hstore;
+       ?column?        
+-----------------------
+ "a"=>[1, 2], "b"=>"c"
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>23'::hstore;
+       ?column?        
+-----------------------
+ "a"=>[1, 2], "b"=>"c"
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>{1,2}'::hstore;
+            ?column?            
+--------------------------------
+ "a"=>[1, 2], "b"=>"c", "v"=>23
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'a=>{1,2}'::hstore;
+     ?column?      
+-------------------
+ "b"=>"c", "v"=>23
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v'::hstore;
+          ?column?           
+-----------------------------
+ ["a", [1, 2], 23, "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v=>23'::hstore;
+        ?column?         
+-------------------------
+ ["a", [1, 2], "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,23]'::hstore;
+        ?column?         
+-------------------------
+ ["a", [1, 2], "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,{1,2}]'::hstore;
+      ?column?       
+---------------------
+ ["a", 23, "b", "c"]
+(1 row)
+
+--joining
+SELECT 'aa=>1 , b=>2, cq=>3'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+                   ?column?                    
+-----------------------------------------------
+ "1"=>2, "b"=>"g", "aa"=>1, "cq"=>"l", "fg"=>f
+(1 row)
+
+SELECT '{aa,1 , b,2, cq,3}'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+                            ?column?                            
+----------------------------------------------------------------
+ ["aa", 1, "b", 2, "cq", 3, "cq", "l", "b", "g", "fg", f, 1, 2]
+(1 row)
+
+--slice
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
+   slice_array    
+------------------
+ {NULL,NULL,NULL}
+(1 row)
+
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+   slice_array    
+------------------
+ {NULL,NULL,NULL}
+(1 row)
+
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['b','c']);
+ slice_array 
+-------------
+ {2,3}
+(1 row)
+
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+ slice_array 
+-------------
+ {b,c}
+(1 row)
+
+SELECT slice_array(hstore 'aa=>1, b=>{2=>1}, c=>{1,2}', ARRAY['b','c']);
+      slice_array      
+-----------------------
+ {"\"2\"=>1","[1, 2]"}
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['g','h','i']);
+ slice 
+-------
+ 
+(1 row)
+
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+ slice 
+-------
+ 
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['b','c']);
+     slice      
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+   slice    
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>{2=>1}, c=>{1,2}}', ARRAY['b','c']);
+           slice            
+----------------------------
+ "b"=>{"2"=>1}, "c"=>[1, 2]
+(1 row)
+
+--to array
+SELECT %% 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL';
+                ?column?                
+----------------------------------------
+ {b,"[\"a\", \"n\"]",aa,1,cq,l,fg,NULL}
+(1 row)
+
+SELECT %% '{aa,1, cq,l, b,g, fg,NULL}';
+        ?column?         
+-------------------------
+ {aa,1,cq,l,b,g,fg,NULL}
+(1 row)
+
+SELECT hstore_to_matrix( 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL');
+                hstore_to_matrix                
+------------------------------------------------
+ {{b,"[\"a\", \"n\"]"},{aa,1},{cq,l},{fg,NULL}}
+(1 row)
+
+SELECT hstore_to_matrix( '{aa,1, cq,l, b,g, fg,NULL}');
+        hstore_to_matrix         
+---------------------------------
+ {{aa,1},{cq,l},{b,g},{fg,NULL}}
+(1 row)
+
+--contains
+SELECT 'a=>b'::hstore @> 'a=>b, c=>b';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>b'::hstore @> 'a=>b';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{2,1}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1=>2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1=>2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{a,b}'::hstore @> '{a,b, c,b}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{a,b, c,b}'::hstore @> '{a,b}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{a,b, c,{1,2}}'::hstore @> '{a,{1,2}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{a,b, c,{1,2}}'::hstore @> '{b,{1,2}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{3}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{c=>3}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},3}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+-- %>
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'n';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'a';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'b';
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'c';
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd';
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd' %> '1';
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[1,2,3,{a,b}]'::hstore %> '1';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '["1",2,3,{a,b}]'::hstore %> '1';
+ ?column? 
+----------
+ "1"
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 3;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 2;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 1;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 0;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 3;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 2;
+ ?column? 
+----------
+ "c"
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 1;
+ ?column? 
+----------
+ "b"
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 0;
+ ?column? 
+----------
+ "a"
+(1 row)
+
+-- ->
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 3;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 2;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 1;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 0;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 3;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 2;
+ ?column? 
+----------
+ c
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 1;
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 0;
+ ?column? 
+----------
+ a
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -6;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -5;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -4;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -3;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -2;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -1;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -6;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -5;
+ ?column? 
+----------
+ a
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -4;
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -3;
+ ?column? 
+----------
+ c
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -2;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -1;
+ ?column? 
+----------
+ 
+(1 row)
+
+-- #>
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{a}';
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c}';
+ ?column?  
+-----------
+ [1, 2, 3]
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 0}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 1}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 2}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 3}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -1}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -2}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -3}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -4}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{0}';
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{3}';
+ ?column? 
+----------
+ [3, 4]
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4}';
+  ?column?   
+-------------
+ "5"=>"five"
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4,5}';
+ ?column? 
+----------
+ five
+(1 row)
+
+-- #%>
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{a}';
+ ?column? 
+----------
+ "b"
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c}';
+ ?column?  
+-----------
+ [1, 2, 3]
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 0}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 1}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 2}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 3}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -1}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -2}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -3}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -4}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{0}';
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{3}';
+ ?column? 
+----------
+ [3, 4]
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4}';
+  ?column?   
+-------------
+ "5"=>"five"
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4,5}';
+ ?column? 
+----------
+ "five"
+(1 row)
+
+-- ?
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 5;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 0;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 5;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 0;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -6;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -5;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -6;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -5;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{0}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{a}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{b}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 0}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 1}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 2}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 3}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -1}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -2}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -3}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -4}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -5}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{0}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{3}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4,5}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+--deep delete
+SELECT 'a=>1'::hstore #- '{x}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{a}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{NULL}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a}';
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b}';
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c}';
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{x,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{a,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{NULL,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT '[a]'::hstore #- '{2}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a]'::hstore #- '{1}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a]'::hstore #- '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore #- '{-1}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore #- '{-2}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{3}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{2}';
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{1}';
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{0}';
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-1}';
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-2}';
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-3}';
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-4}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{0,0}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{x}';
+                             ?column?                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{a}';
+                         ?column?                          
+-----------------------------------------------------------
+ "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b}';
+                       ?column?                       
+------------------------------------------------------
+ "a"=>1, "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c}';
+                      ?column?                      
+----------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d}';
+                   ?column?                    
+-----------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, 0}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}' #- '{b, -1}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>NULL, "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 1}';
+                           ?column?                            
+---------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>NULL, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 2}';
+                             ?column?                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, -2}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 1}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>NULL}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}' #- '{d, 1, 0}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>NULL}, "n"=>NULL
+(1 row)
+
+-- delete(int)
+SELECT '[a,b,c]'::hstore - 3;
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 2;
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 1;
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 0;
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -1;
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -2;
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -3;
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -4;
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 3;
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 2;
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 1;
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 0;
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -1;
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -2;
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -3;
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -4;
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+--replace
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{1,2,3}');
+                                replace                                 
+------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>[1, 2, 3]
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b,-1}', '{1,2,3}');
+                                  replace                                  
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, [1, 2, 3]], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1,0}', '{1,2,3}');
+                                  replace                                  
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[[1, 2, 3], 3]}, "n"=>NULL
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,NULL,0}', '{1,2,3}');
+                              replace                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+--deep concat
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'n=>not_null');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>"not_null"
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'n=>not_null');
+                                  concat_path                                   
+--------------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>{"n"=>"not_null"}
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'not_null');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>"not_null"
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{not_null}');
+                                concat_path                                
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>["not_null"]
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'b=>{3,4}');
+                            concat_path                            
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[3, 4], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b}', '{3,4}');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2, 3, 4], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '{4,5}');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3, 4, 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '4=>5');
+                                concat_path                                
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3, "4", 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d}', '2=>{4,5}');
+                                  concat_path                                   
+--------------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3], "2"=>[4, 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{NULL,1}', '4=>5');
+                            concat_path                            
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+--cast 
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::text)::hstore AS err;
+ERROR:  bad hstore representation
+DETAIL:  syntax error, unexpected STRING_P, expecting '}' or ',' at end of input
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json)::hstore AS ok;
+                         ok                         
+----------------------------------------------------
+ "f2"=>{"f3"=>1}, "f4"=>{"f5"=>99, "f6"=>"stringy"}
+(1 row)
+
+--hvals
+SELECT q->'tags' FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore) AS q;
+ ?column? 
+----------
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+                      q                       
+----------------------------------------------
+ [{"sh"=>2, "tags"=>1}, {"sh"=>4, "tags"=>3}]
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+ "sh"=>4, "tags"=>3
+(2 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+ q 
+---
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+                                q                                
+-----------------------------------------------------------------
+ "1"=>"first", "a"=>{"b"=>"c", "c"=>"b"}, "b"=>[1, 2], "c"=>"cc"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+    q    
+---------
+ "first"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+         q          
+--------------------
+ "b"=>"c", "c"=>"b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+   q    
+--------
+ [1, 2]
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+  q   
+------
+ "cc"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "first"
+ "b"=>"c", "c"=>"b"
+ [1, 2]
+ "cc"
+(4 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+  q  
+-----
+ "b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+  q  
+-----
+ "b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+ q 
+---
+ 1
+ 2
+(2 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+ q 
+---
+ 2
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+    q    
+---------
+ "first"
+ 2
+(2 rows)
+
+--svals path
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+                      q                       
+----------------------------------------------
+ [{"sh"=>2, "tags"=>1}, {"sh"=>4, "tags"=>3}]
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+ "sh"=>4, "tags"=>3
+(2 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+ q 
+---
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+                                q                                
+-----------------------------------------------------------------
+ "1"=>"first", "a"=>{"b"=>"c", "c"=>"b"}, "b"=>[1, 2], "c"=>"cc"
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+   q   
+-------
+ first
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+         q          
+--------------------
+ "b"=>"c", "c"=>"b"
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+   q    
+--------
+ [1, 2]
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+ q  
+----
+ cc
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ first
+ "b"=>"c", "c"=>"b"
+ [1, 2]
+ cc
+(4 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+ q 
+---
+ b
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+ q 
+---
+ b
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+ q 
+---
+ 1
+ 2
+(2 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+ q 
+---
+ 2
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+   q   
+-------
+ first
+ 2
+(2 rows)
+
+--each
+SELECT * FROM each('a=>b, c=>cc'::hstore) AS q;
+ key | value 
+-----+-------
+ a   | b
+ c   | cc
+(2 rows)
+
+SELECT * FROM each('[a, b, c, cc]'::hstore) AS q;
+ key | value 
+-----+-------
+     | a
+     | b
+     | c
+     | cc
+(4 rows)
+
+SELECT * FROM each('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+ key |              value               
+-----+----------------------------------
+ 1   | first
+ a   | "1"=>"first", "b"=>"c", "c"=>"b"
+ b   | [1, 2]
+ c   | cc
+ n   | 
+(5 rows)
+
+SELECT * FROM each_hstore('a=>b, c=>cc'::hstore) AS q;
+ key | value 
+-----+-------
+ a   | "b"
+ c   | "cc"
+(2 rows)
+
+SELECT * FROM each_hstore('[a, b, c, cc]'::hstore) AS q;
+ key | value 
+-----+-------
+     | "a"
+     | "b"
+     | "c"
+     | "cc"
+(4 rows)
+
+SELECT * FROM each_hstore('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+ key |              value               
+-----+----------------------------------
+ 1   | "first"
+ a   | "1"=>"first", "b"=>"c", "c"=>"b"
+ b   | [1, 2]
+ c   | "cc"
+ n   | 
+(5 rows)
+
+--decoration
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]]'::hstore AS h, '[a, {b=>c}, [c, d, e]]'::hstore AS a;
+                  h                   |                 a                  
+--------------------------------------+------------------------------------
+ "a"=>1, "b"=>{"c"=>3}, "d"=>[4, [5]] | ["a", {"b"=>"c"}, ["c", "d", "e"]]
+(1 row)
+
+SET hstore.pretty_print = true;
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]], e=>[1,2,3,4], f=>g, g=>j'::hstore AS h, 
+	   '[a, {b=>c, c=>d}, [c, d, e, [1,2], h, {f=>g, g=>f}]]'::hstore AS a;
+     h      |           a            
+------------+------------------------
+ "a"=>1,   +| [                     +
+ "b"=>     +|     "a",              +
+ {         +|     {                 +
+     "c"=>3+|         "b"=>"c",     +
+ },        +|         "c"=>"d"      +
+ "d"=>     +|     },                +
+ [         +|     [                 +
+     4,    +|         "c",          +
+     [     +|         "d",          +
+         5 +|         "e",          +
+     ]     +|         [             +
+ ],        +|             1,        +
+ "e"=>     +|             2         +
+ [         +|         ],            +
+     1,    +|         "h",          +
+     2,    +|         {             +
+     3,    +|             "f"=>"g", +
+     4     +|             "g"=>f    +
+ ],        +|         }             +
+ "f"=>"g", +|     ]                 +
+ "g"=>"j"   | ]
+(1 row)
+
+RESET hstore.pretty_print;
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore);
+                             hstore_print                              
+-----------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>[1, 2, 3, "3", "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true );
+                           hstore_print                            
+-------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>[1, 2, 3, 3, "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true );
+                              hstore_print                               
+-------------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>[1, 2, 3, "3", "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, array_curly_braces := true );
+                             hstore_print                              
+-----------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>{1, 2, 3, "3", "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": "f", "123": "string", "arr": [1, 2, 3, "3", "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, loose := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": false, "123": "string", "arr": [1, 2, 3, 3, "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, root_hash_decorated := true );
+                                 hstore_print                                  
+-------------------------------------------------------------------------------
+ {"a": true, "f": true, "t": "f", "123": "string", "arr": [1, 2, 3, "3", "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, array_curly_braces := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": "f", "123": "string", "arr": {1, 2, 3, "3", "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, root_hash_decorated := true );
+                            hstore_print                             
+---------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>[1, 2, 3, 3, "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, array_curly_braces := true );
+                           hstore_print                            
+-------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>{1, 2, 3, 3, "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true );
+                              hstore_print                               
+-------------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>{1, 2, 3, "3", "x"}}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true, loose := true);
+                            hstore_print                             
+---------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>{1, 2, 3, 3, "x"}}
+(1 row)
+
diff --git a/contrib/hstore/expected/types.out b/contrib/hstore/expected/types.out
new file mode 100644
index 0000000..8233d52
--- /dev/null
+++ b/contrib/hstore/expected/types.out
@@ -0,0 +1,732 @@
+SELECT '"foo"=>true'::hstore;
+  hstore  
+----------
+ "foo"=>t
+(1 row)
+
+SELECT 'foo=>true'::hstore;
+  hstore  
+----------
+ "foo"=>t
+(1 row)
+
+SELECT '"true"=>true'::hstore;
+  hstore   
+-----------
+ "true"=>t
+(1 row)
+
+SELECT 'true=>true'::hstore;
+  hstore   
+-----------
+ "true"=>t
+(1 row)
+
+SELECT '"t"=>true'::hstore;
+ hstore 
+--------
+ "t"=>t
+(1 row)
+
+SELECT 't=>true'::hstore;
+ hstore 
+--------
+ "t"=>t
+(1 row)
+
+SELECT '"false"=>true'::hstore;
+   hstore   
+------------
+ "false"=>t
+(1 row)
+
+SELECT 'false=>true'::hstore;
+   hstore   
+------------
+ "false"=>t
+(1 row)
+
+SELECT '"f"=>true'::hstore;
+ hstore 
+--------
+ "f"=>t
+(1 row)
+
+SELECT 'f=>true'::hstore;
+ hstore 
+--------
+ "f"=>t
+(1 row)
+
+SELECT '"foo"=>false'::hstore;
+  hstore  
+----------
+ "foo"=>f
+(1 row)
+
+SELECT 'foo=>false'::hstore;
+  hstore  
+----------
+ "foo"=>f
+(1 row)
+
+SELECT '"false"=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT 'false=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT '"t"=>false'::hstore;
+ hstore 
+--------
+ "t"=>f
+(1 row)
+
+SELECT 't=>false'::hstore;
+ hstore 
+--------
+ "t"=>f
+(1 row)
+
+SELECT '"false"=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT 'false=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT '"f"=>false'::hstore;
+ hstore 
+--------
+ "f"=>f
+(1 row)
+
+SELECT 'f=>false'::hstore;
+ hstore 
+--------
+ "f"=>f
+(1 row)
+
+SELECT '"1"=>x'::hstore;
+  hstore  
+----------
+ "1"=>"x"
+(1 row)
+
+SELECT '1=>x'::hstore;
+  hstore  
+----------
+ "1"=>"x"
+(1 row)
+
+SELECT 'foo=>1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>1.'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>1.0'::hstore;
+   hstore   
+------------
+ "foo"=>1.0
+(1 row)
+
+SELECT 'foo=>1.01'::hstore;
+   hstore    
+-------------
+ "foo"=>1.01
+(1 row)
+
+SELECT 'foo=>1.01e'::hstore;
+     hstore     
+----------------
+ "foo"=>"1.01e"
+(1 row)
+
+SELECT 'foo=>1.01e1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>1.01e+1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>1.01e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>0.101
+(1 row)
+
+SELECT 'foo=>.1'::hstore;
+   hstore   
+------------
+ "foo"=>0.1
+(1 row)
+
+SELECT 'foo=>.1e'::hstore;
+    hstore    
+--------------
+ "foo"=>".1e"
+(1 row)
+
+SELECT 'foo=>.1e1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>.1e+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+1.'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+1.0'::hstore;
+   hstore   
+------------
+ "foo"=>1.0
+(1 row)
+
+SELECT 'foo=>+1.01'::hstore;
+   hstore    
+-------------
+ "foo"=>1.01
+(1 row)
+
+SELECT 'foo=>+1.01e'::hstore;
+     hstore      
+-----------------
+ "foo"=>"+1.01e"
+(1 row)
+
+SELECT 'foo=>+1.01e1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>+1.01e+1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>+1.01e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>0.101
+(1 row)
+
+SELECT 'foo=>+.1'::hstore;
+   hstore   
+------------
+ "foo"=>0.1
+(1 row)
+
+SELECT 'foo=>+.1e'::hstore;
+    hstore     
+---------------
+ "foo"=>"+.1e"
+(1 row)
+
+SELECT 'foo=>+.1e1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+.1e+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>-1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-1.'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-1.0'::hstore;
+   hstore    
+-------------
+ "foo"=>-1.0
+(1 row)
+
+SELECT 'foo=>-1.01'::hstore;
+    hstore    
+--------------
+ "foo"=>-1.01
+(1 row)
+
+SELECT 'foo=>-1.01e'::hstore;
+     hstore      
+-----------------
+ "foo"=>"-1.01e"
+(1 row)
+
+SELECT 'foo=>-1.01e1'::hstore;
+    hstore    
+--------------
+ "foo"=>-10.1
+(1 row)
+
+SELECT 'foo=>-1.01e+1'::hstore;
+    hstore    
+--------------
+ "foo"=>-10.1
+(1 row)
+
+SELECT 'foo=>-1.01e-1'::hstore;
+    hstore     
+---------------
+ "foo"=>-0.101
+(1 row)
+
+SELECT 'foo=>-.1'::hstore;
+   hstore    
+-------------
+ "foo"=>-0.1
+(1 row)
+
+SELECT 'foo=>-.1e'::hstore;
+    hstore     
+---------------
+ "foo"=>"-.1e"
+(1 row)
+
+SELECT 'foo=>-.1e1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-.1e+1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-.1e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>-0.01
+(1 row)
+
+SELECT 'foo=>1e2000'::hstore;
+     hstore      
+-----------------
+ "foo"=>"1e2000"
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'foo';
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'bar';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 0;
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 1;
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'foo';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'bar';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 0;
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 1;
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 0}';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 1}';
+    ?column?    
+----------------
+ 0.000000000001
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'foo';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'foo';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'foo';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 1;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'foo';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 1;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 1}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT hstore_typeof('a=>b') AS hash;
+ hash 
+------
+ hash
+(1 row)
+
+SELECT hstore_typeof('{a=>b}') AS hash;
+ hash 
+------
+ hash
+(1 row)
+
+SELECT hstore_typeof('{a, b}') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('{{a=>b}}') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('[a, b]') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('') AS "NULL";
+ NULL 
+------
+ 
+(1 row)
+
+SELECT hstore_typeof('NULL') AS "null";
+ null 
+------
+ null
+(1 row)
+
+SELECT hstore_typeof('1.0') AS numeric;
+ numeric 
+---------
+ numeric
+(1 row)
+
+SELECT hstore_typeof('t') AS bool;
+ bool 
+------
+ bool
+(1 row)
+
+SELECT hstore_typeof('f') AS bool;
+ bool 
+------
+ bool
+(1 row)
+
+SELECT hstore('xxx', 't'::bool);
+  hstore  
+----------
+ "xxx"=>t
+(1 row)
+
+SELECT hstore('xxx', 'f'::bool);
+  hstore  
+----------
+ "xxx"=>f
+(1 row)
+
+SELECT hstore('xxx', 3.14);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore('xxx', 3.14::numeric);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore('xxx', '3.14'::numeric);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore(NULL);
+ hstore 
+--------
+ 
+(1 row)
+
+SELECT hstore('NULL');
+ hstore 
+--------
+ NULL
+(1 row)
+
+SELECT hstore('t'::bool) AS "true", hstore('f'::bool) AS "false";
+ true | false 
+------+-------
+ t    | f
+(1 row)
+
+SELECT hstore(3.14), hstore(3.14::numeric), hstore('3.14'::numeric);
+ hstore | hstore | hstore 
+--------+--------+--------
+ 3.14   | 3.14   | 3.14
+(1 row)
+
+SELECT hstore('xxx', 'foo=>t, bar=>3.14, zzz=>xxx'::hstore);
+                    hstore                    
+----------------------------------------------
+ "xxx"=>{"bar"=>3.14, "foo"=>t, "zzz"=>"xxx"}
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int2[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int4[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int8[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float4[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float8[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,f},{f,t,NULL}}'::bool[]);
+      array_to_hstore      
+---------------------------
+ [[t, t, f], [f, t, NULL]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::text[]);
+           array_to_hstore           
+-------------------------------------
+ [["1", "1", "4"], ["23", "3", "5"]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::varchar[]);
+           array_to_hstore           
+-------------------------------------
+ [["1", "1", "4"], ["23", "3", "5"]]
+(1 row)
+
+SELECT array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]);
+                       array_to_hstore                       
+-------------------------------------------------------------
+ [[[1, 11], [1, 1], [4, 41], [23, 231]], [[3, 31], [5, 51]]]
+(1 row)
+
+SELECT hstore('array', array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]));
+                                hstore                                
+----------------------------------------------------------------------
+ "array"=>[[[1, 11], [1, 1], [4, 41], [23, 231]], [[3, 31], [5, 51]]]
+(1 row)
+
diff --git a/contrib/hstore/hstore--1.2--1.3.sql b/contrib/hstore/hstore--1.2--1.3.sql
new file mode 100644
index 0000000..b913a08
--- /dev/null
+++ b/contrib/hstore/hstore--1.2--1.3.sql
@@ -0,0 +1,338 @@
+/* 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 FUNCTION hstore2jsonb(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore2jsonb'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS jsonb)
+  WITH FUNCTION hstore2jsonb(hstore);
+
+CREATE FUNCTION jsonb2hstore(jsonb)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'jsonb2hstore'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (jsonb AS hstore)
+  WITH FUNCTION jsonb2hstore(jsonb);
+
diff --git a/contrib/hstore/hstore--1.2.sql b/contrib/hstore/hstore--1.2.sql
deleted file mode 100644
index f415a72..0000000
--- a/contrib/hstore/hstore--1.2.sql
+++ /dev/null
@@ -1,537 +0,0 @@
-/* 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
-
-CREATE TYPE hstore;
-
-CREATE FUNCTION hstore_in(cstring)
-RETURNS hstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_out(hstore)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_recv(internal)
-RETURNS hstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_send(hstore)
-RETURNS bytea
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE TYPE hstore (
-        INTERNALLENGTH = -1,
-        INPUT = hstore_in,
-        OUTPUT = hstore_out,
-        RECEIVE = hstore_recv,
-        SEND = hstore_send,
-        STORAGE = extended
-);
-
-CREATE FUNCTION hstore_version_diag(hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_version_diag'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION fetchval(hstore,text)
-RETURNS text
-AS 'MODULE_PATHNAME','hstore_fetchval'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR -> (
-	LEFTARG = hstore,
-	RIGHTARG = text,
-	PROCEDURE = fetchval
-);
-
-CREATE FUNCTION slice_array(hstore,text[])
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_slice_to_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR -> (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = slice_array
-);
-
-CREATE FUNCTION slice(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_slice_to_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION isexists(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION exist(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists'
-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'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?| (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = exists_any,
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION exists_all(hstore,text[])
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_all'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?& (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = exists_all,
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION isdefined(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_defined'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION defined(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_defined'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = text,
-	PROCEDURE = delete
-);
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = delete
-);
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = delete
-);
-
-CREATE FUNCTION hs_concat(hstore,hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_concat'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR || (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_concat
-);
-
-CREATE FUNCTION hs_contains(hstore,hstore)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_contains'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hs_contained(hstore,hstore)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_contained'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR @> (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contains,
-	COMMUTATOR = '<@',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE OPERATOR <@ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contained,
-	COMMUTATOR = '@>',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
--- obsolete:
-CREATE OPERATOR @ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contains,
-	COMMUTATOR = '~',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contained,
-	COMMUTATOR = '@',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION tconvert(text,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_text'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_text'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text[],text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_arrays'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null)
-
-CREATE FUNCTION hstore(text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_array'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE CAST (text[] AS hstore)
-  WITH FUNCTION hstore(text[]);
-
-CREATE FUNCTION hstore_to_json(hstore)
-RETURNS json
-AS 'MODULE_PATHNAME', 'hstore_to_json'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE CAST (hstore AS json)
-  WITH FUNCTION hstore_to_json(hstore);
-
-CREATE FUNCTION hstore_to_json_loose(hstore)
-RETURNS json
-AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION hstore(record)
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_record'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype)
-
-CREATE FUNCTION hstore_to_array(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_to_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %% (
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_to_array
-);
-
-CREATE FUNCTION hstore_to_matrix(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_to_matrix'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %# (
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_to_matrix
-);
-
-CREATE FUNCTION akeys(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_akeys'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION avals(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_avals'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION skeys(hstore)
-RETURNS setof text
-AS 'MODULE_PATHNAME','hstore_skeys'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION svals(hstore)
-RETURNS setof text
-AS 'MODULE_PATHNAME','hstore_svals'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION each(IN hs hstore,
-    OUT key text,
-    OUT value text)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME','hstore_each'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION populate_record(anyelement,hstore)
-RETURNS anyelement
-AS 'MODULE_PATHNAME', 'hstore_populate_record'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore)
-
-CREATE OPERATOR #= (
-	LEFTARG = anyelement,
-	RIGHTARG = hstore,
-	PROCEDURE = populate_record
-);
-
--- btree support
-
-CREATE FUNCTION hstore_eq(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_eq'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_ne(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_ne'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_gt(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_gt'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_ge(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_ge'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_lt(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_lt'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_le(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_le'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_cmp(hstore,hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_cmp'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR = (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_eq,
-       COMMUTATOR = =,
-       NEGATOR = <>,
-       RESTRICT = eqsel,
-       JOIN = eqjoinsel,
-       MERGES,
-       HASHES
-);
-CREATE OPERATOR <> (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_ne,
-       COMMUTATOR = <>,
-       NEGATOR = =,
-       RESTRICT = neqsel,
-       JOIN = neqjoinsel
-);
-
--- the comparison operators have funky names (and are undocumented)
--- in an attempt to discourage anyone from actually using them. they
--- only exist to support the btree opclass
-
-CREATE OPERATOR #<# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_lt,
-       COMMUTATOR = #>#,
-       NEGATOR = #>=#,
-       RESTRICT = scalarltsel,
-       JOIN = scalarltjoinsel
-);
-CREATE OPERATOR #<=# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_le,
-       COMMUTATOR = #>=#,
-       NEGATOR = #>#,
-       RESTRICT = scalarltsel,
-       JOIN = scalarltjoinsel
-);
-CREATE OPERATOR #># (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_gt,
-       COMMUTATOR = #<#,
-       NEGATOR = #<=#,
-       RESTRICT = scalargtsel,
-       JOIN = scalargtjoinsel
-);
-CREATE OPERATOR #>=# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_ge,
-       COMMUTATOR = #<=#,
-       NEGATOR = #<#,
-       RESTRICT = scalargtsel,
-       JOIN = scalargtjoinsel
-);
-
-CREATE OPERATOR CLASS btree_hstore_ops
-DEFAULT FOR TYPE hstore USING btree
-AS
-	OPERATOR	1	#<# ,
-	OPERATOR	2	#<=# ,
-	OPERATOR	3	= ,
-	OPERATOR	4	#>=# ,
-	OPERATOR	5	#># ,
-	FUNCTION	1	hstore_cmp(hstore,hstore);
-
--- hash support
-
-CREATE FUNCTION hstore_hash(hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_hash'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR CLASS hash_hstore_ops
-DEFAULT FOR TYPE hstore USING hash
-AS
-	OPERATOR	1	= ,
-	FUNCTION	1	hstore_hash(hstore);
-
--- GiST support
-
-CREATE TYPE ghstore;
-
-CREATE FUNCTION ghstore_in(cstring)
-RETURNS ghstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION ghstore_out(ghstore)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE TYPE ghstore (
-        INTERNALLENGTH = -1,
-        INPUT = ghstore_in,
-        OUTPUT = ghstore_out
-);
-
-CREATE FUNCTION ghstore_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_union(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_same(internal, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR CLASS gist_hstore_ops
-DEFAULT FOR TYPE hstore USING gist
-AS
-	OPERATOR        7       @> ,
-	OPERATOR        9       ?(hstore,text) ,
-	OPERATOR        10      ?|(hstore,text[]) ,
-	OPERATOR        11      ?&(hstore,text[]) ,
-        --OPERATOR        8       <@ ,
-        OPERATOR        13      @ ,
-        --OPERATOR        14      ~ ,
-        FUNCTION        1       ghstore_consistent (internal, internal, int, oid, internal),
-        FUNCTION        2       ghstore_union (internal, internal),
-        FUNCTION        3       ghstore_compress (internal),
-        FUNCTION        4       ghstore_decompress (internal),
-        FUNCTION        5       ghstore_penalty (internal, internal, internal),
-        FUNCTION        6       ghstore_picksplit (internal, internal),
-        FUNCTION        7       ghstore_same (internal, internal, internal),
-        STORAGE         ghstore;
-
--- GIN support
-
-CREATE FUNCTION gin_extract_hstore(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR CLASS gin_hstore_ops
-DEFAULT FOR TYPE hstore USING gin
-AS
-	OPERATOR        7       @>,
-	OPERATOR        9       ?(hstore,text),
-	OPERATOR        10      ?|(hstore,text[]),
-	OPERATOR        11      ?&(hstore,text[]),
-	FUNCTION        1       bttextcmp(text,text),
-	FUNCTION        2       gin_extract_hstore(internal, internal),
-	FUNCTION        3       gin_extract_hstore_query(internal, internal, int2, internal, internal),
-	FUNCTION        4       gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
-	STORAGE         text;
diff --git a/contrib/hstore/hstore--1.3.sql b/contrib/hstore/hstore--1.3.sql
new file mode 100644
index 0000000..153cb74
--- /dev/null
+++ b/contrib/hstore/hstore--1.3.sql
@@ -0,0 +1,854 @@
+/* contrib/hstore/hstore--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION hstore" to load this file. \quit
+
+CREATE TYPE hstore;
+
+CREATE FUNCTION hstore_in(cstring)
+RETURNS hstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_out(hstore)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_recv(internal)
+RETURNS hstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_send(hstore)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE hstore (
+        INTERNALLENGTH = -1,
+        INPUT = hstore_in,
+        OUTPUT = hstore_out,
+        RECEIVE = hstore_recv,
+        SEND = hstore_send,
+        STORAGE = extended
+);
+
+CREATE FUNCTION hstore_version_diag(hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_version_diag'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION fetchval(hstore,text)
+RETURNS text
+AS 'MODULE_PATHNAME','hstore_fetchval'
+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_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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR -> (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = slice_array
+);
+
+CREATE FUNCTION slice(hstore,text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_slice_to_hstore'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION isexists(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION exist(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ? (
+	LEFTARG = hstore,
+	RIGHTARG = text,
+	PROCEDURE = exist,
+	RESTRICT = contsel,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ?| (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = exists_any,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION exists_all(hstore,text[])
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists_all'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ?& (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = exists_all,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION isdefined(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_defined'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION defined(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_defined'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,text)
+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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,hstore)
+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[],
+	PROCEDURE = delete
+);
+
+CREATE OPERATOR - (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR || (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hs_contained(hstore,hstore)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_contained'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR @> (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contains,
+	COMMUTATOR = '<@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contained,
+	COMMUTATOR = '@>',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+-- obsolete:
+CREATE OPERATOR @ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contains,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contained,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION tconvert(text,text)
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_from_text'
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
+
+CREATE FUNCTION hstore(text,text)
+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'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null)
+
+CREATE FUNCTION hstore(text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_array'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (text[] AS hstore)
+  WITH FUNCTION hstore(text[]);
+
+CREATE FUNCTION hstore_to_json(hstore)
+RETURNS json
+AS 'MODULE_PATHNAME', 'hstore_to_json'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS json)
+  WITH FUNCTION hstore_to_json(hstore);
+
+CREATE FUNCTION hstore2jsonb(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore2jsonb'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS jsonb)
+  WITH FUNCTION hstore2jsonb(hstore);
+
+CREATE FUNCTION jsonb2hstore(jsonb)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'jsonb2hstore'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (jsonb AS hstore)
+  WITH FUNCTION jsonb2hstore(jsonb);
+
+CREATE FUNCTION hstore_to_json_loose(hstore)
+RETURNS json
+AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION hstore(record)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_record'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype)
+
+CREATE FUNCTION hstore_to_array(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_to_array'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR %% (
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_to_array
+);
+
+CREATE FUNCTION hstore_to_matrix(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_to_matrix'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR %# (
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_to_matrix
+);
+
+CREATE FUNCTION akeys(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_akeys'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION avals(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_avals'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION skeys(hstore)
+RETURNS setof text
+AS 'MODULE_PATHNAME','hstore_skeys'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION svals(hstore)
+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)
+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'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore)
+
+CREATE OPERATOR #= (
+	LEFTARG = anyelement,
+	RIGHTARG = hstore,
+	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)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_eq'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_ne(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_ne'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_gt(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_gt'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_ge(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_ge'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_lt(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_lt'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_le(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_le'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_cmp(hstore,hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_cmp'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR = (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_eq,
+       COMMUTATOR = =,
+       NEGATOR = <>,
+       RESTRICT = eqsel,
+       JOIN = eqjoinsel,
+       MERGES,
+       HASHES
+);
+CREATE OPERATOR <> (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_ne,
+       COMMUTATOR = <>,
+       NEGATOR = =,
+       RESTRICT = neqsel,
+       JOIN = neqjoinsel
+);
+
+-- the comparison operators have funky names (and are undocumented)
+-- in an attempt to discourage anyone from actually using them. they
+-- only exist to support the btree opclass
+
+CREATE OPERATOR #<# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_lt,
+       COMMUTATOR = #>#,
+       NEGATOR = #>=#,
+       RESTRICT = scalarltsel,
+       JOIN = scalarltjoinsel
+);
+CREATE OPERATOR #<=# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_le,
+       COMMUTATOR = #>=#,
+       NEGATOR = #>#,
+       RESTRICT = scalarltsel,
+       JOIN = scalarltjoinsel
+);
+CREATE OPERATOR #># (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_gt,
+       COMMUTATOR = #<#,
+       NEGATOR = #<=#,
+       RESTRICT = scalargtsel,
+       JOIN = scalargtjoinsel
+);
+CREATE OPERATOR #>=# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_ge,
+       COMMUTATOR = #<=#,
+       NEGATOR = #<#,
+       RESTRICT = scalargtsel,
+       JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR CLASS btree_hstore_ops
+DEFAULT FOR TYPE hstore USING btree
+AS
+	OPERATOR	1	#<# ,
+	OPERATOR	2	#<=# ,
+	OPERATOR	3	= ,
+	OPERATOR	4	#>=# ,
+	OPERATOR	5	#># ,
+	FUNCTION	1	hstore_cmp(hstore,hstore);
+
+-- hash support
+
+CREATE FUNCTION hstore_hash(hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_hash'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR CLASS hash_hstore_ops
+DEFAULT FOR TYPE hstore USING hash
+AS
+	OPERATOR	1	= ,
+	FUNCTION	1	hstore_hash(hstore);
+
+-- GiST support
+
+CREATE TYPE ghstore;
+
+CREATE FUNCTION ghstore_in(cstring)
+RETURNS ghstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION ghstore_out(ghstore)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE ghstore (
+        INTERNALLENGTH = -1,
+        INPUT = ghstore_in,
+        OUTPUT = ghstore_out
+);
+
+CREATE FUNCTION ghstore_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_union(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_same(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR CLASS gist_hstore_ops
+DEFAULT FOR TYPE hstore USING gist
+AS
+	OPERATOR        7       @> ,
+	OPERATOR        9       ?(hstore,text) ,
+	OPERATOR        10      ?|(hstore,text[]) ,
+	OPERATOR        11      ?&(hstore,text[]) ,
+        --OPERATOR        8       <@ ,
+        OPERATOR        13      @ ,
+        --OPERATOR        14      ~ ,
+        FUNCTION        1       ghstore_consistent (internal, internal, int, oid, internal),
+        FUNCTION        2       ghstore_union (internal, internal),
+        FUNCTION        3       ghstore_compress (internal),
+        FUNCTION        4       ghstore_decompress (internal),
+        FUNCTION        5       ghstore_penalty (internal, internal, internal),
+        FUNCTION        6       ghstore_picksplit (internal, internal),
+        FUNCTION        7       ghstore_same (internal, internal, internal),
+        STORAGE         ghstore;
+
+-- GIN support: default opclass
+
+CREATE FUNCTION gin_extract_hstore(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR CLASS gin_hstore_ops
+DEFAULT FOR TYPE hstore USING gin
+AS
+	OPERATOR        7       @>,
+	OPERATOR        9       ?(hstore,text),
+	OPERATOR        10      ?|(hstore,text[]),
+	OPERATOR        11      ?&(hstore,text[]),
+	FUNCTION        1       bttextcmp(text,text),
+	FUNCTION        2       gin_extract_hstore(internal, internal),
+	FUNCTION        3       gin_extract_hstore_query(internal, internal, int2, internal, internal),
+	FUNCTION        4       gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
+	STORAGE         text;
+
+-- 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;
+
+
+
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index 9daf5e2..dcc3b68 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,5 +1,5 @@
 # hstore extension
 comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/hstore'
 relocatable = true
diff --git a/contrib/hstore/hstore.h b/contrib/hstore/hstore.h
index 23c8a6f..197289b 100644
--- a/contrib/hstore/hstore.h
+++ b/contrib/hstore/hstore.h
@@ -4,9 +4,7 @@
 #ifndef __HSTORE_H__
 #define __HSTORE_H__
 
-#include "fmgr.h"
-#include "utils/array.h"
-
+#include "utils/jsonb.h"
 
 /*
  * HEntry: there is one of these for each key _and_ value in an hstore
@@ -15,158 +13,126 @@
  * by subtraction from the previous entry.	the ISFIRST flag lets us tell
  * whether there is a previous entry.
  */
-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]))
+
+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_ISCALAR is only used in send/recv */
+#define HENTRY_ISHASH		JENTRY_ISOBJECT
+#define HENTRY_ISARRAY		JENTRY_ISARRAY
+#define HENTRY_ISCALAR		JENTRY_ISCALAR
+
+#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_)
 
 /*
- * 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
+ * determined by the size of "endpos" (ie HENTRY_POSMASK)
  */
-#define HSTORE_MAX_KEY_LEN 0x3FFFFFFF
-#define HSTORE_MAX_VALUE_LEN 0x3FFFFFFF
+#define HSTORE_MAX_KEY_LEN 		HENTRY_POSMASK
+#define HSTORE_MAX_VALUE_LEN 	HENTRY_POSMASK
 
-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;
+typedef Jsonb HStore;
 
 /*
  * it's not possible to get more than 2^28 items into an hstore,
  * so we reserve the top few bits of the size field. See hstore_compat.c
  * for one reason why.	Some bits are left for future use here.
  */
-#define HS_FLAG_NEWVERSION 0x80000000
+#define HS_FLAG_NEWVERSION 		0x80000000
+#define HS_FLAG_ARRAY			JB_FLAG_ARRAY
+#define HS_FLAG_HASH			JB_FLAG_OBJECT
+#define HS_FLAG_SCALAR			JB_FLAG_SCALAR
 
-#define HS_COUNT(hsp_) ((hsp_)->size_ & 0x0FFFFFFF)
-#define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION)
+#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 HSHRDSIZE	(sizeof(HStore))
-#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) )
+/* DatumGetHStoreP includes support for reading old-format hstore values */
+extern HStore *hstoreUpgrade(Datum orig);
 
-/* note multiple evaluations of x */
-#define ARRPTR(x)		( (HEntry*) ( (HStore*)(x) + 1 ) )
-#define STRPTR(x)		( (char*)(ARRPTR(x) + HS_COUNT((HStore*)(x)) * 2) )
+#define DatumGetHStoreP(d) hstoreUpgrade(d)
 
-/* 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 PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x))
 
-/*
- * 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.
- */
+typedef JsonbPair HStorePair;
+typedef JsonbValue HStoreValue;
 
-/*
- * 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)
+/* JsonbValue.type renaming */
+#define hsvNull		jbvNull
+#define hsvString	jbvString
+#define hsvNumeric	jbvNumeric
+#define hsvBool		jbvBool
+#define hsvArray	jbvArray
+#define hsvHash		jbvHash
+#define hsvBinary	jbvBinary
 
 /*
- * add one key/item pair, from a Pairs structure, into an
- * under-construction hstore
+ * hstore support functions, they are mostly the same as jsonb
  */
-#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)
 
-/* DatumGetHStoreP includes support for reading old-format hstore values */
-extern HStore *hstoreUpgrade(Datum orig);
+#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 DatumGetHStoreP(d) hstoreUpgrade(d)
+#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))
 
-#define PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x))
+#define compareHStoreBinaryValue(a, b)				compareJsonbBinaryValue((a), (b))
+#define compareHStoreValue(a, b)					compareJsonbValue((a), (b))
 
+#define findUncompressedHStoreValueByValue(buffer, flags, lowbound, key)	\
+	findUncompressedJsonbValueByValue((buffer), (flags), (lowbound), (key))
+#define findUncompressedHStoreValue(buffer, flags, lowbound, key, keylen)	\
+	findUncompressedJsonbValue((buffer), (flags), (lowbound), (key), (keylen))
 
-/*
- * 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;
+#define getHStoreValue(buffer, flags, i)			getJsonbValue((buffer), (flags), (i))
+
+typedef ToJsonbState ToHStoreState;
+#define pushHStoreValue(state, r /* WHS_* */, v)	pushJsonbValue((state), (r), (v))
+
+extern uint32 compressHStore(HStoreValue *v, char *buffer);
+
+typedef JsonbIterator HStoreIterator;
+
+#define	HStoreIteratorInit(buffer)					JsonbIteratorInit(buffer)
 
-extern int	hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen);
-extern HStore *hstorePairs(Pairs *pairs, int32 pcount, int32 buflen);
+#define HStoreIteratorGet(it, v, skipNested)	JsonbIteratorGet((it), (v), (skipNested))
 
-extern size_t hstoreCheckKeyLen(size_t len);
-extern size_t hstoreCheckValLen(size_t len);
+text* HStoreValueToText(HStoreValue *v);
 
-extern int	hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen);
-extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs);
+extern HStoreValue* parseHStore(const char *str, int len, bool json);
+
+#define uniqueHStoreValue(v) uniqueJsonbValue(v)
 
 #define HStoreContainsStrategyNumber	7
 #define HStoreExistsStrategyNumber		9
@@ -194,4 +160,18 @@ extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs);
 	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__ */
diff --git a/contrib/hstore/hstore_compat.c b/contrib/hstore/hstore_compat.c
index 6327a8e..0e18505 100644
--- a/contrib/hstore/hstore_compat.c
+++ b/contrib/hstore/hstore_compat.c
@@ -105,9 +105,41 @@ typedef struct
 				pos:31;
 } HOldEntry;
 
-static int	hstoreValidNewFormat(HStore *hs);
-static int	hstoreValidOldFormat(HStore *hs);
+/*
+ * 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))
 
+#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_)]))
+
+/* 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.
@@ -116,7 +148,7 @@ static int	hstoreValidOldFormat(HStore *hs);
  *	2 = exactly valid
  */
 static int
-hstoreValidNewFormat(HStore *hs)
+hstoreValidNewFormat(HStoreV2 *hs)
 {
 	int			count = HS_COUNT(hs);
 	HEntry	   *entries = ARRPTR(hs);
@@ -168,7 +200,7 @@ hstoreValidNewFormat(HStore *hs)
  *	2 = exactly valid
  */
 static int
-hstoreValidOldFormat(HStore *hs)
+hstoreValidOldFormat(HStoreV2 *hs)
 {
 	int			count = hs->size_;
 	HOldEntry  *entries = (HOldEntry *) ARRPTR(hs);
@@ -235,16 +267,26 @@ hstoreValidOldFormat(HStore *hs)
 HStore *
 hstoreUpgrade(Datum orig)
 {
-	HStore	   *hs = (HStore *) PG_DETOAST_DATUM(orig);
+	HStoreV2	   *hs = (HStoreV2 *) PG_DETOAST_DATUM(orig);
 	int			valid_new;
 	int			valid_old;
 	bool		writable;
 
 	/* Return immediately if no conversion needed */
-	if ((hs->size_ & HS_FLAG_NEWVERSION) ||
+	if (VARSIZE_ANY(hs) <= VARHDRSZ ||
+		(hs->size_ & HS_FLAG_NEWVERSION) ||
 		hs->size_ == 0 ||
 		(VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0]))))
-		return hs;
+	{
+		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;
+	}
 
 	valid_new = hstoreValidNewFormat(hs);
 	valid_old = hstoreValidOldFormat(hs);
@@ -266,7 +308,7 @@ hstoreUpgrade(Datum orig)
 				HS_SETCOUNT(hs, HS_COUNT(hs));
 				HS_FIXSIZE(hs, HS_COUNT(hs));
 			}
-			return hs;
+			return (HStore*)hs;
 		}
 		else
 		{
@@ -323,7 +365,7 @@ hstoreUpgrade(Datum orig)
 	 */
 
 	if (!writable)
-		hs = (HStore *) PG_DETOAST_DATUM_COPY(orig);
+		hs = (HStoreV2 *) PG_DETOAST_DATUM_COPY(orig);
 
 	{
 		int			count = hs->size_;
@@ -352,7 +394,7 @@ hstoreUpgrade(Datum orig)
 		HS_FIXSIZE(hs, count);
 	}
 
-	return hs;
+	return (HStore*)hs;
 }
 
 
@@ -361,7 +403,7 @@ Datum		hstore_version_diag(PG_FUNCTION_ARGS);
 Datum
 hstore_version_diag(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = (HStore *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+	HStoreV2	   *hs = (HStoreV2 *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
 	int			valid_new = hstoreValidNewFormat(hs);
 	int			valid_old = hstoreValidOldFormat(hs);
 
diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c
index 2007801..5cb831a 100644
--- a/contrib/hstore/hstore_gin.c
+++ b/contrib/hstore/hstore_gin.c
@@ -6,21 +6,11 @@
 #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);
 
@@ -41,37 +31,82 @@ 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:
+			cstr = DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric)));
+			item = makeitem(cstr, strlen(cstr), flag);
+			break;
+		case hsvString:
+			item = makeitem(v->string.val, v->string.len, flag);
+			break;
+		default:
+			elog(ERROR, "Wrong hstore 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;
-	HEntry	   *hsent = ARRPTR(hs);
-	char	   *ptr = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			i;
+	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;
 
-	*nentries = 2 * count;
-	if (count)
-		entries = (Datum *) palloc(sizeof(Datum) * 2 * count);
-
-	for (i = 0; i < count; ++i)
+	if (total == 0)
 	{
-		text	   *item;
+		*nentries = 0;
+		PG_RETURN_POINTER(NULL);
+	}
 
-		item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i),
-						KEYFLAG);
-		entries[2 * i] = PointerGetDatum(item);
+	entries = (Datum *) palloc(sizeof(Datum) * total);
 
-		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);
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, false)) != 0)
+	{
+		if (i >= total)
+		{
+			total *= 2;
+			entries = (Datum *) repalloc(entries, sizeof(Datum) * total);
+		}
+
+		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;
+		}
 	}
 
+	*nentries = i;
+
 	PG_RETURN_POINTER(entries);
 }
 
@@ -129,7 +164,8 @@ 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);
 		}
 
@@ -211,3 +247,196 @@ 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:
+			COMP_CRC32(stack->hash_state,
+					   VARDATA_ANY(v->numeric), VARSIZE_ANY_EXHDR(v->numeric));
+			break;
+		case hsvString:
+			COMP_CRC32(stack->hash_state, v->string.val, v->string.len);
+			break;
+		default:
+			elog(ERROR, "Shouldn't take hash of array");
+			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:
+				path_crc32 = stack->hash_state;
+				hash_value(&v, stack);
+				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);
+}
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index 9001180..05b3f0a 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -6,8 +6,8 @@
 #include "access/gist.h"
 #include "access/skey.h"
 #include "catalog/pg_type.h"
+#include "utils/pg_crc.h"
 
-#include "crc32.h"
 #include "hstore.h"
 
 /* bigint defines */
@@ -105,6 +105,66 @@ 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:
+			COMP_CRC32(crc, VARDATA_ANY(v->numeric), VARSIZE_ANY_EXHDR(v->numeric));
+			break;
+		default:
+			elog(PANIC, "impossible value %d", v->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)
 {
@@ -113,25 +173,26 @@ ghstore_compress(PG_FUNCTION_ARGS)
 
 	if (entry->leafkey)
 	{
-		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;
+		GISTTYPE   		*res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+		HStore	   		*val = DatumGetHStoreP(entry->key);
 
 		SET_VARSIZE(res, CALCGTSIZE(0));
 
-		for (i = 0; i < count; ++i)
+		if (!HS_ISEMPTY(val))
 		{
-			int			h;
+			int				r;
+			HStoreIterator	*it = HStoreIteratorInit(VARDATA(val));
+			HStoreValue		v;
 
-			h = crc32_sz((char *) HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i));
-			HASH(GETSIGN(res), h);
-			if (!HS_VALISNULL(hsent, i))
+			while((r = HStoreIteratorGet(&it, &v, false)) != 0)
 			{
-				h = crc32_sz((char *) HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i));
-				HASH(GETSIGN(res), h);
+				if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) &&
+					v.type != hsvNull)
+				{
+					int   h = crc32_HStoreValue(&v, r);
+
+					HASH(GETSIGN(res), h);
+				}
 			}
 		}
 
@@ -396,7 +457,8 @@ 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)))
@@ -410,7 +472,8 @@ 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);
@@ -490,7 +553,6 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	PG_RETURN_POINTER(v);
 }
 
-
 Datum
 ghstore_consistent(PG_FUNCTION_ARGS)
 {
@@ -513,82 +575,123 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 	if (strategy == HStoreContainsStrategyNumber ||
 		strategy == HStoreOldContainsStrategyNumber)
 	{
-		HStore	   *query = PG_GETARG_HS(1);
-		HEntry	   *qe = ARRPTR(query);
-		char	   *qv = STRPTR(query);
-		int			count = HS_COUNT(query);
+		BITVECP		qe;
 		int			i;
 
-		for (i = 0; res && i < count; ++i)
+		qe = fcinfo->flinfo->fn_extra;
+		if (qe == NULL)
 		{
-			int			crc = crc32_sz((char *) HS_KEY(qe, qv, i), HS_KEYLEN(qe, i));
+			HStore	   		*query = PG_GETARG_HS(1);
+
+			qe = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(BITVEC));
+			memset(qe, 0, sizeof(BITVEC));
 
-			if (GETBIT(sign, HASHVAL(crc)))
+			if (!HS_ISEMPTY(query))
 			{
-				if (!HS_VALISNULL(qe, i))
+				int				r;
+				HStoreIterator	*it = HStoreIteratorInit(VARDATA(query));
+				HStoreValue		v;
+
+				while((r = HStoreIteratorGet(&it, &v, false)) != 0)
 				{
-					crc = crc32_sz((char *) HS_VAL(qe, qv, i), HS_VALLEN(qe, i));
-					if (!GETBIT(sign, HASHVAL(crc)))
-						res = false;
+					if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) && v.type != hsvNull)
+					{
+						int   crc = crc32_HStoreValue(&v, r);
+
+						HASH(qe, crc);
+					}
 				}
 			}
-			else
+
+			fcinfo->flinfo->fn_extra = qe;
+		}
+
+		LOOPBYTE
+		{
+			if ((sign[i] & qe[i]) != qe[i])
+			{
 				res = false;
+				break;
+			}
 		}
 	}
 	else if (strategy == HStoreExistsStrategyNumber)
 	{
-		text	   *query = PG_GETARG_TEXT_PP(1);
-		int			crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
+		int 	*qval;
 
-		res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
-	}
-	else if (strategy == HStoreExistsAllStrategyNumber)
-	{
-		ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
-		Datum	   *key_datums;
-		bool	   *key_nulls;
-		int			key_count;
-		int			i;
-
-		deconstruct_array(query,
-						  TEXTOID, -1, false, 'i',
-						  &key_datums, &key_nulls, &key_count);
-
-		for (i = 0; res && i < key_count; ++i)
+		qval = fcinfo->flinfo->fn_extra;
+		if (qval == NULL)
 		{
-			int			crc;
+			text	   *query = PG_GETARG_TEXT_PP(1);
+			int			crc = crc32_Key(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
 
-			if (key_nulls[i])
-				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (!(GETBIT(sign, HASHVAL(crc))))
-				res = FALSE;
+			qval = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(*qval));
+			*qval = HASHVAL(crc);
+
+			fcinfo->flinfo->fn_extra = qval;
 		}
+
+		res = (GETBIT(sign, *qval)) ? true : false;
 	}
-	else if (strategy == HStoreExistsAnyStrategyNumber)
+	else if (strategy == HStoreExistsAllStrategyNumber ||
+			 strategy == HStoreExistsAnyStrategyNumber)
 	{
-		ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
-		Datum	   *key_datums;
-		bool	   *key_nulls;
-		int			key_count;
-		int			i;
+		BITVECP	arrentry;
+		int		i;
 
-		deconstruct_array(query,
-						  TEXTOID, -1, false, 'i',
-						  &key_datums, &key_nulls, &key_count);
+		arrentry = fcinfo->flinfo->fn_extra;
+		if (arrentry == NULL)
+		{
+			ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
+			Datum	   *key_datums;
+			bool	   *key_nulls;
+			int			key_count;
 
-		res = FALSE;
+			arrentry = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+										  sizeof(BITVEC));
+			memset(arrentry, 0, sizeof(BITVEC));
 
-		for (i = 0; !res && i < key_count; ++i)
+			deconstruct_array(query,
+							  TEXTOID, -1, false, 'i',
+							  &key_datums, &key_nulls, &key_count);
+
+			for (i = 0; i < key_count; ++i)
+			{
+				int			crc;
+
+				if (key_nulls[i])
+					continue;
+				crc = crc32_Key(VARDATA(key_datums[i]),
+								VARSIZE(key_datums[i]) - VARHDRSZ);
+				HASH(arrentry, crc);
+			}
+
+			fcinfo->flinfo->fn_extra = arrentry;
+		}
+
+		if (strategy == HStoreExistsAllStrategyNumber)
 		{
-			int			crc;
+			LOOPBYTE
+			{
+				if ((sign[i] & arrentry[i]) != arrentry[i])
+				{
+					res = false;
+					break;
+				}
+			}
+		}
+		else /* HStoreExistsAnyStrategyNumber */
+		{
+			res = false;
 
-			if (key_nulls[i])
-				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (GETBIT(sign, HASHVAL(crc)))
-				res = TRUE;
+			LOOPBYTE
+			{
+				if (sign[i] & arrentry[i])
+				{
+					res = true;
+					break;
+				}
+			}
 		}
 	}
 	else
diff --git a/contrib/hstore/hstore_gram.y b/contrib/hstore/hstore_gram.y
new file mode 100644
index 0000000..d0883fa
--- /dev/null
+++ b/contrib/hstore/hstore_gram.y
@@ -0,0 +1,341 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 973a126..0f6887c 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -7,12 +7,15 @@
 
 #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 "utils/builtins.h"
+#include "utils/fmgroids.h"
 #include "utils/json.h"
+#include "utils/guc.h"
 #include "utils/lsyscache.h"
+#include "utils/syscache.h"
 #include "utils/typcache.h"
 
 #include "hstore.h"
@@ -22,507 +25,453 @@ 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))
 
-typedef struct
+static void recvHStore(StringInfo buf, HStoreValue *v, uint32 level,
+					   uint32 header);
+
+typedef enum HStoreOutputKind {
+	JsonOutput = 0x01,
+	LooseOutput = 0x02,
+	ArrayCurlyBraces = 0x04,
+	RootHashDecorated = 0x08,
+	PrettyPrint = 0x10
+} HStoreOutputKind;
+
+static char* HStoreToCString(StringInfo out, char *in,
+							 int len /* just estimation */, HStoreOutputKind kind);
+
+static size_t
+hstoreCheckKeyLen(size_t len)
 {
-	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
+	if (len > HSTORE_MAX_KEY_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for hstore key")));
+	return len;
+}
 
-static bool
-get_val(HSParser *state, bool ignoreeq, bool *escaped)
+static size_t
+hstoreCheckValLen(size_t len)
 {
-	int			st = GV_WAITVAL;
+	if (len > HSTORE_MAX_VALUE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for hstore value")));
+	return len;
+}
 
-	state->wordlen = 32;
-	state->cur = state->word = palloc(state->wordlen);
-	*escaped = false;
 
-	while (1)
+static HStore*
+hstoreDump(HStoreValue *p)
+{
+	uint32			buflen;
+	HStore	 	   *out;
+
+	if (p == NULL)
 	{
-		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__);
+		buflen = 0;
+		out = palloc(VARHDRSZ);
+	}
+	else
+	{
+		buflen = VARHDRSZ + p->size;
+		out = palloc(buflen);
+		SET_VARSIZE(out, buflen);
 
-		state->ptr++;
+		buflen = compressHStore(p, VARDATA(out));
 	}
+	SET_VARSIZE(out, buflen + VARHDRSZ);
+
+	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)));
 }
 
-#define WKEY	0
-#define WVAL	1
-#define WEQ 2
-#define WGT 3
-#define WDEL	4
+static void
+recvHStoreValue(StringInfo buf, HStoreValue *v, uint32 level, int c)
+{
+	uint32  hentry = c & HENTRY_TYPEMASK;
 
+	if (c == -1 /* compatibility */ || hentry == HENTRY_ISNULL)
+	{
+		v->type = hsvNull;
+		v->size = sizeof(HEntry);
+	}
+	else if (hentry == HENTRY_ISHASH || hentry == HENTRY_ISARRAY ||
+			 hentry == HENTRY_ISCALAR)
+	{
+		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, &c);
+		v->string.len = hstoreCheckKeyLen(c);
+		v->size = sizeof(HEntry) + v->string.len;
+	}
+	else
+	{
+		elog(ERROR, "bogus input");
+	}
+}
 
 static void
-parse_hstore(HSParser *state)
+recvHStore(StringInfo buf, HStoreValue *v, uint32 level, uint32 header)
 {
-	int			st = WKEY;
-	bool		escaped = false;
+	uint32	hentry;
+	uint32	i;
+
+	hentry = header & HENTRY_TYPEMASK;
 
-	state->plen = 16;
-	state->pairs = (Pairs *) palloc(sizeof(Pairs) * state->plen);
-	state->pcur = 0;
-	state->ptr = state->begin;
-	state->word = NULL;
+	if (level == 0 && hentry == 0)
+		hentry = HENTRY_ISHASH; /* old version */
 
-	while (1)
+	v->size = 3 * sizeof(HEntry);
+	if (hentry == HENTRY_ISHASH)
 	{
-		if (st == WKEY)
+		v->type = hsvHash;
+		v->hash.npairs = header & HS_COUNT_MASK;
+		if (v->hash.npairs > 0)
 		{
-			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)
+			v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+			for(i=0; i<v->hash.npairs; i++)
 			{
-				state->word[4] = '\0';
-				if (0 == pg_strcasecmp(state->word, "null"))
-					state->pairs[state->pcur].isnull = true;
+				recvHStoreValue(buf, &v->hash.pairs[i].key, level,
+								pq_getmsgint(buf, 4));
+				if (v->hash.pairs[i].key.type != hsvString)
+					elog(ERROR, "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;
 			}
-			state->word = NULL;
-			state->pcur++;
-			st = WDEL;
+
+			uniqueHStoreValue(v);
 		}
-		else if (st == WDEL)
+	}
+	else if (hentry == HENTRY_ISARRAY || hentry == HENTRY_ISCALAR)
+	{
+		v->type = hsvArray;
+		v->array.nelems = header & HS_COUNT_MASK;
+		v->array.scalar = (hentry == HENTRY_ISCALAR) ? true : false;
+
+		if (v->array.scalar && v->array.nelems != 1)
+			elog(ERROR, "bogus input");
+
+		if (v->array.nelems > 0)
 		{
-			if (*(state->ptr) == ',')
-			{
-				st = WKEY;
-			}
-			else if (*(state->ptr) == '\0')
-			{
-				return;
-			}
-			else if (!isspace((unsigned char) *(state->ptr)))
+			v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+			for(i=0; i<v->array.nelems; i++)
 			{
-				elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
+				recvHStoreValue(buf, v->array.elems + i, level,
+								pq_getmsgint(buf, 4));
+				v->size += v->array.elems[i].size;
 			}
 		}
-		else
-			elog(ERROR, "Unknown state %d at line %d in file '%s'", st, __LINE__, __FILE__);
-
-		state->ptr++;
+	}
+	else
+	{
+			elog(ERROR, "bogus input");
 	}
 }
 
-static int
-comparePairs(const void *a, const void *b)
+PG_FUNCTION_INFO_V1(hstore_recv);
+Datum		hstore_recv(PG_FUNCTION_ARGS);
+Datum
+hstore_recv(PG_FUNCTION_ARGS)
 {
-	const Pairs *pa = a;
-	const Pairs *pb = b;
-
-	if (pa->keylen == pb->keylen)
-	{
-		int			res = memcmp(pa->key, pb->key, pa->keylen);
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	HStoreValue	v;
 
-		if (res)
-			return res;
+	recvHStore(buf, &v, 0, pq_getmsgint(buf, 4));
 
-		/* 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;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-/*
- * 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)
+PG_FUNCTION_INFO_V1(hstore_from_text);
+Datum		hstore_from_text(PG_FUNCTION_ARGS);
+Datum
+hstore_from_text(PG_FUNCTION_ARGS)
 {
-	Pairs	   *ptr,
-			   *res;
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	*buflen = 0;
-	if (l < 2)
+	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))
 	{
-		if (l == 1)
-			*buflen = a->keylen + ((a->isnull) ? 0 : a->vallen);
-		return l;
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
 	}
-
-	qsort((void *) a, l, sizeof(Pairs), comparePairs);
-	ptr = a + 1;
-	res = a;
-	while (ptr - a < l)
+	else
 	{
-		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));
-		}
+		text	   	*val = NULL;
 
-		ptr++;
+		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);
 	}
 
-	*buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
-	return res + 1 - a;
-}
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-size_t
-hstoreCheckKeyLen(size_t len)
-{
-	if (len > HSTORE_MAX_KEY_LEN)
-		ereport(ERROR,
-				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
-				 errmsg("string too long for hstore key")));
-	return len;
-}
-
-size_t
-hstoreCheckValLen(size_t len)
-{
-	if (len > HSTORE_MAX_VALUE_LEN)
-		ereport(ERROR,
-				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
-				 errmsg("string too long for hstore value")));
-	return len;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-HStore *
-hstorePairs(Pairs *pairs, int32 pcount, int32 buflen)
+PG_FUNCTION_INFO_V1(hstore_from_bool);
+Datum		hstore_from_bool(PG_FUNCTION_ARGS);
+Datum
+hstore_from_bool(PG_FUNCTION_ARGS)
 {
-	HStore	   *out;
-	HEntry	   *entry;
-	char	   *ptr;
-	char	   *buf;
-	int32		len;
-	int32		i;
-
-	len = CALCDATASIZE(pcount, buflen);
-	out = palloc(len);
-	SET_VARSIZE(out, len);
-	HS_SETCOUNT(out, pcount);
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	if (pcount == 0)
-		return out;
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	entry = ARRPTR(out);
-	buf = ptr = STRPTR(out);
+	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);
 
-	for (i = 0; i < pcount; i++)
-		HS_ADDITEM(entry, buf, ptr, pairs[i]);
+	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);
+	}
 
-	HS_FINALIZE(out, pcount, buf, ptr);
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-	return out;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_in);
-Datum		hstore_in(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_from_numeric);
+Datum		hstore_from_numeric(PG_FUNCTION_ARGS);
 Datum
-hstore_in(PG_FUNCTION_ARGS)
+hstore_from_numeric(PG_FUNCTION_ARGS)
 {
-	HSParser	state;
-	int32		buflen;
-	HStore	   *out;
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	state.begin = PG_GETARG_CSTRING(0);
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	parse_hstore(&state);
+	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);
 
-	state.pcur = hstoreUniquePairs(state.pairs, state.pcur, &buflen);
+	if (PG_ARGISNULL(1))
+	{
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
+	}
+	else
+	{
+		pair.value.type = hsvNumeric;
+		pair.value.numeric = PG_GETARG_NUMERIC(1);
+		pair.value.size = sizeof(HEntry) + sizeof(HEntry) +
+							VARSIZE_ANY(pair.value.numeric);
+	}
 
-	out = hstorePairs(state.pairs, state.pcur, buflen);
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_recv);
-Datum		hstore_recv(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_from_th);
+Datum		hstore_from_th(PG_FUNCTION_ARGS);
 Datum
-hstore_recv(PG_FUNCTION_ARGS)
+hstore_from_th(PG_FUNCTION_ARGS)
 {
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
-	int32		i;
-	int32		pcount;
-	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	pcount = pq_getmsgint(buf, 4);
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	if (pcount == 0)
+	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))
 	{
-		out = hstorePairs(NULL, 0, 0);
-		PG_RETURN_POINTER(out);
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
 	}
+	else
+	{
+		HStore	   	*val = NULL;
 
-	pairs = palloc(pcount * sizeof(Pairs));
+		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;
+	}
 
-	for (i = 0; i < pcount; ++i)
-	{
-		int			rawlen = pq_getmsgint(buf, 4);
-		int			len;
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-		if (rawlen < 0)
-			ereport(ERROR,
-					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-					 errmsg("null value not allowed for hstore key")));
+	PG_RETURN_POINTER(hstoreDump(&v));
+}
 
-		pairs[i].key = pq_getmsgtext(buf, rawlen, &len);
-		pairs[i].keylen = hstoreCheckKeyLen(len);
-		pairs[i].needfree = true;
+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;
 
-		rawlen = pq_getmsgint(buf, 4);
-		if (rawlen < 0)
-		{
-			pairs[i].val = NULL;
-			pairs[i].vallen = 0;
-			pairs[i].isnull = true;
-		}
-		else
-		{
-			pairs[i].val = pq_getmsgtext(buf, rawlen, &len);
-			pairs[i].vallen = hstoreCheckValLen(len);
-			pairs[i].isnull = false;
-		}
+	if (PG_ARGISNULL(0))
+	{
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
 	}
+	else
+	{
+		text	*scalar;
 
-	pcount = hstoreUniquePairs(pairs, pcount, &buflen);
+		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);
+	}
 
-	out = hstorePairs(pairs, pcount, buflen);
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&a));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_from_text);
-Datum		hstore_from_text(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_scalar_from_bool);
+Datum		hstore_scalar_from_bool(PG_FUNCTION_ARGS);
 Datum
-hstore_from_text(PG_FUNCTION_ARGS)
+hstore_scalar_from_bool(PG_FUNCTION_ARGS)
 {
-	text	   *key;
-	text	   *val = NULL;
-	Pairs		p;
-	HStore	   *out;
+	HStoreValue	a, v;
 
 	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
+	{
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
+	}
+	else
+	{
+		v.type = hsvBool;
+		v.boolean = PG_GETARG_BOOL(0);
+		v.size = sizeof(HEntry);
+	}
 
-	p.needfree = false;
-	key = PG_GETARG_TEXT_PP(0);
-	p.key = VARDATA_ANY(key);
-	p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	if (PG_ARGISNULL(1))
+	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))
 	{
-		p.vallen = 0;
-		p.isnull = true;
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
 	}
 	else
 	{
-		val = PG_GETARG_TEXT_PP(1);
-		p.val = VARDATA_ANY(val);
-		p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
-		p.isnull = false;
+		v.type = hsvNumeric;
+		v.numeric = PG_GETARG_NUMERIC(0);
+		v.size = VARSIZE_ANY(v.numeric) + 2*sizeof(HEntry);
 	}
 
-	out = hstorePairs(&p, 1, p.keylen + p.vallen);
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&a));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_from_arrays);
 Datum		hstore_from_arrays(PG_FUNCTION_ARGS);
 Datum
 hstore_from_arrays(PG_FUNCTION_ARGS)
 {
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
+	HStoreValue v;
 	Datum	   *key_datums;
 	bool	   *key_nulls;
 	int			key_count;
@@ -589,7 +538,10 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
 		Assert(key_count == value_count);
 	}
 
-	pairs = palloc(key_count * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2 * sizeof(HEntry);
+	v.hash.pairs = palloc(key_count * sizeof(*v.hash.pairs));
+	v.hash.npairs = key_count;
 
 	for (i = 0; i < key_count; ++i)
 	{
@@ -598,31 +550,34 @@ 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])
 		{
-			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;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
 		else
 		{
-			pairs[i].key = VARDATA_ANY(key_datums[i]);
-			pairs[i].val = VARDATA_ANY(value_datums[i]);
-			pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
-			pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i]));
-			pairs[i].isnull = false;
-			pairs[i].needfree = false;
+			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;
 		}
+
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
 	}
 
-	key_count = hstoreUniquePairs(pairs, key_count, &buflen);
+	uniqueHStoreValue(&v);
 
-	out = hstorePairs(pairs, key_count, buflen);
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
 
@@ -634,9 +589,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
 	ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
 	int			ndims = ARR_NDIM(in_array);
 	int			count;
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
+	HStoreValue	v;
 	Datum	   *in_datums;
 	bool	   *in_nulls;
 	int			in_count;
@@ -647,8 +600,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
 	switch (ndims)
 	{
 		case 0:
-			out = hstorePairs(NULL, 0, 0);
-			PG_RETURN_POINTER(out);
+			PG_RETURN_POINTER(hstoreDump(NULL));
 
 		case 1:
 			if ((ARR_DIMS(in_array)[0]) % 2)
@@ -676,7 +628,10 @@ hstore_from_array(PG_FUNCTION_ARGS)
 
 	count = in_count / 2;
 
-	pairs = palloc(count * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2*sizeof(HEntry);
+	v.hash.npairs = count;
+	v.hash.pairs = palloc(count * sizeof(HStorePair));
 
 	for (i = 0; i < count; ++i)
 	{
@@ -685,31 +640,33 @@ 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])
 		{
-			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;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
 		else
 		{
-			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.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;
 		}
-	}
 
-	count = hstoreUniquePairs(pairs, count, &buflen);
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
+	}
 
-	out = hstorePairs(pairs, count, buflen);
+	uniqueHStoreValue(&v);
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
 /* most of hstore_from_record is shamelessly swiped from record_out */
@@ -739,19 +696,17 @@ Datum
 hstore_from_record(PG_FUNCTION_ARGS)
 {
 	HeapTupleHeader rec;
-	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;
+	HStore		   *out;
+	HStoreValue	   v;
+	Oid				tupType;
+	int32			tupTypmod;
+	TupleDesc		tupdesc;
+	HeapTupleData 	tuple;
+	RecordIOData   *my_extra;
+	int				ncolumns;
+	int				i;
+	Datum	   	   *values;
+	bool	   	   *nulls;
 
 	if (PG_ARGISNULL(0))
 	{
@@ -807,7 +762,10 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	pairs = palloc(ncolumns * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2*sizeof(HEntry);
+	v.hash.npairs = ncolumns;
+	v.hash.pairs = palloc(ncolumns * sizeof(HStorePair));
 
 	if (rec)
 	{
@@ -829,7 +787,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		nulls = NULL;
 	}
 
-	for (i = 0, j = 0; i < ncolumns; ++i)
+	for (i = 0; i < ncolumns; ++i)
 	{
 		ColumnIOData *column_info = &my_extra->columns[i];
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
@@ -839,46 +797,65 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		if (tupdesc->attrs[i]->attisdropped)
 			continue;
 
-		pairs[j].key = NameStr(tupdesc->attrs[i]->attname);
-		pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(tupdesc->attrs[i]->attname)));
+		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;
 
 		if (!nulls || nulls[i])
 		{
-			pairs[j].val = NULL;
-			pairs[j].vallen = 4;
-			pairs[j].isnull = true;
-			pairs[j].needfree = false;
-			++j;
-			continue;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
-
-		/*
-		 * Convert the column value to text
-		 */
-		if (column_info->column_type != column_type)
+		else
 		{
-			bool		typIsVarlena;
+			/*
+			 * 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 (column_type == NUMERICOID)
+			{	/* XXX float... int... */
+				v.hash.pairs[i].value.type = hsvNumeric;
+				v.hash.pairs[i].value.numeric = DatumGetNumeric(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;
+				}
 
-			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]);
 
-		value = OutputFunctionCall(&column_info->proc, values[i]);
+				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;
+			}
+		}
 
-		pairs[j].val = value;
-		pairs[j].vallen = hstoreCheckValLen(strlen(value));
-		pairs[j].isnull = false;
-		pairs[j].needfree = false;
-		++j;
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
 	}
 
-	ncolumns = hstoreUniquePairs(pairs, j, &buflen);
+	uniqueHStoreValue(&v);
 
-	out = hstorePairs(pairs, ncolumns, buflen);
+	out = hstoreDump(&v);
 
 	ReleaseTupleDesc(tupdesc);
 
@@ -893,8 +870,6 @@ 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;
@@ -940,8 +915,6 @@ 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
@@ -949,7 +922,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 	 * domain nulls.
 	 */
 
-	if (HS_COUNT(hs) == 0 && rec)
+	if (HS_ISEMPTY(hs) && rec)
 		PG_RETURN_POINTER(rec);
 
 	tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
@@ -1013,9 +986,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 	{
 		ColumnIOData *column_info = &my_extra->columns[i];
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
-		char	   *value;
-		int			idx;
-		int			vallen;
+		HStoreValue	*v = NULL;
 
 		/* Ignore dropped columns in datatype */
 		if (tupdesc->attrs[i]->attisdropped)
@@ -1024,9 +995,12 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 			continue;
 		}
 
-		idx = hstoreFindKey(hs, 0,
-							NameStr(tupdesc->attrs[i]->attname),
-							strlen(NameStr(tupdesc->attrs[i]->attname)));
+		if (!HS_ISEMPTY(hs))
+		{
+			char *key = NameStr(tupdesc->attrs[i]->attname);
+
+			v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HASH, NULL, key, strlen(key));
+		}
 
 		/*
 		 * we can't just skip here if the key wasn't found since we might have
@@ -1036,7 +1010,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 (idx < 0 && rec)
+		if (v == NULL && rec)
 			continue;
 
 		/*
@@ -1052,7 +1026,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 			column_info->column_type = column_type;
 		}
 
-		if (idx < 0 || HS_VALISNULL(entries, idx))
+		if (v == NULL || v->type == hsvNull)
 		{
 			/*
 			 * need InputFunctionCall to happen even for nulls, so that domain
@@ -1065,12 +1039,29 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 		}
 		else
 		{
-			vallen = HS_VALLEN(entries, idx);
-			value = palloc(1 + vallen);
-			memcpy(value, HS_VAL(entries, ptr, idx), vallen);
-			value[vallen] = 0;
+			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(PANIC, "Wrong hstore");
 
-			values[i] = InputFunctionCall(&column_info->proc, value,
+			values[i] = InputFunctionCall(&column_info->proc, s,
 										  column_info->typioparam,
 										  tupdesc->attrs[i]->atttypmod);
 			nulls[i] = false;
@@ -1084,125 +1075,377 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(rettuple));
 }
 
+static void
+printIndent(StringInfo out, bool isRootHash, HStoreOutputKind kind, int level)
+{
+	if (kind & PrettyPrint)
+	{
+		int i;
+
+		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 char *
-cpw(char *dst, char *src, int len)
+static void
+escape_hstore(StringInfo out, char *string, uint32 len)
 {
-	char	   *ptr = src;
+	char       *ptr = string;
 
-	while (ptr - src < len)
+	appendStringInfoCharMacro(out, '"');
+	while (ptr - string < len)
 	{
 		if (*ptr == '"' || *ptr == '\\')
-			*dst++ = '\\';
-		*dst++ = *ptr++;
+			appendStringInfoCharMacro(out, '\\');
+		appendStringInfoCharMacro(out, *ptr);
+		ptr++;
 	}
-	return dst;
+	appendStringInfoCharMacro(out, '"');
 }
 
-PG_FUNCTION_INFO_V1(hstore_out);
-Datum		hstore_out(PG_FUNCTION_ARGS);
-Datum
-hstore_out(PG_FUNCTION_ARGS)
+static void
+putEscapedString(StringInfo out, HStoreOutputKind kind,
+				 char *string, uint32 len)
 {
-	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);
+	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 && JsonbStringIsNumber(string, len))
+			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);
+	}
+}
+
+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(PANIC, "Unknown type");
+	}
+}
+
+static bool
+needBrackets(int level, bool isArray, HStoreOutputKind kind, bool isScalar)
+{
+	bool res;
 
-	if (count == 0)
-		PG_RETURN_CSTRING(pstrdup(""));
+	if (isArray && isScalar)
+		res = false;
+	else if (level == 0)
+		res = (isArray || (kind & RootHashDecorated)) ? true : false;
+	else
+		res = true;
 
-	buflen = 0;
+	return res;
+}
 
-	/*
-	 * 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.
-	 */
+static bool
+isArrayBrackets(HStoreOutputKind kind)
+{
+	return ((kind & ArrayCurlyBraces) == 0) ? true : false;
+}
+		
+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;
 
-	for (i = 0; i < count; i++)
+	if (out == NULL)
+		out = makeStringInfo();
+
+	if (in == NULL)
 	{
-		/* 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));
+		appendStringInfoString(out, "");
+		return out->data;
 	}
 
-	out = ptr = palloc(buflen);
+	enlargeStringInfo(out, (len >= 0) ? len : 64);
+
+	it = HStoreIteratorInit(in);
 
-	for (i = 0; i < count; i++)
+	while((type = HStoreIteratorGet(&it, &v, false)) != 0)
 	{
-		*ptr++ = '"';
-		ptr = cpw(ptr, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		*ptr++ = '"';
-		*ptr++ = '=';
-		*ptr++ = '>';
-		if (HS_VALISNULL(entries, i))
-		{
-			*ptr++ = 'N';
-			*ptr++ = 'U';
-			*ptr++ = 'L';
-			*ptr++ = 'L';
-		}
-		else
+reout:
+		switch(type)
 		{
-			*ptr++ = '"';
-			ptr = cpw(ptr, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
-			*ptr++ = '"';
-		}
+			case WHS_BEGIN_ARRAY:
+				if (first == false)
+				{
+					appendBinaryStringInfo(out, ", ", 2);
+					printCR(out, kind);
+				}
+				first = true;
 
-		if (i + 1 != count)
-		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
+				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(PANIC, "Wrong flags");
 		}
 	}
-	*ptr = '\0';
 
-	PG_RETURN_CSTRING(out);
+	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);
+	}
+
+	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));
+
+	PG_RETURN_CSTRING(out);
+}
 
 PG_FUNCTION_INFO_V1(hstore_send);
 Datum		hstore_send(PG_FUNCTION_ARGS);
 Datum
 hstore_send(PG_FUNCTION_ARGS)
 {
-	HStore	   *in = PG_GETARG_HS(0);
-	int			i;
-	int			count = HS_COUNT(in);
-	char	   *base = STRPTR(in);
-	HEntry	   *entries = ARRPTR(in);
-	StringInfoData buf;
+	HStore	   		*in = PG_GETARG_HS(0);
+	StringInfoData	buf;
 
 	pq_begintypsend(&buf);
 
-	pq_sendint(&buf, count, 4);
-
-	for (i = 0; i < count; i++)
+	if (HS_ISEMPTY(in))
+	{
+		pq_sendint(&buf, 0, 4);
+	}
+	else
 	{
-		int32		keylen = HS_KEYLEN(entries, i);
+		HStoreIterator	*it;
+		int				type;
+		HStoreValue		v;
+		uint32			flag;
+		bytea			*nbuf;
 
-		pq_sendint(&buf, keylen, 4);
-		pq_sendtext(&buf, HS_KEY(entries, base, i), keylen);
-		if (HS_VALISNULL(entries, i))
-		{
-			pq_sendint(&buf, -1, 4);
-		}
-		else
-		{
-			int32		vallen = HS_VALLEN(entries, i);
+		enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */);
 
-			pq_sendint(&buf, vallen, 4);
-			pq_sendtext(&buf, HS_VAL(entries, base, i), vallen);
+		it = HStoreIteratorInit(VARDATA_ANY(in));
+
+		while((type = HStoreIteratorGet(&it, &v, false)) != 0)
+		{
+			switch(type)
+			{
+				case WHS_BEGIN_ARRAY:
+					flag = (v.array.scalar) ? HENTRY_ISCALAR : 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, VARSIZE_ANY(nbuf) | HENTRY_ISNUMERIC, 4);
+							pq_sendbytes(&buf, (char*)nbuf, VARSIZE_ANY(nbuf));
+							break;
+						default:
+							elog(PANIC, "Wrong type: %u", v.type);
+					}
+					break;
+				case WHS_END_ARRAY:
+				case WHS_END_HASH:
+					break;
+				default:
+					elog(PANIC, "Wrong flags");
+			}
 		}
 	}
 
@@ -1224,124 +1467,28 @@ Datum
 hstore_to_json_loose(PG_FUNCTION_ARGS)
 {
 	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);
-	bool		is_number;
-	StringInfo	src,
-				dst;
-
-	if (count == 0)
-		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
-
-	buflen = 3;
+	text	   *out;
 
-	/*
-	 * Formula adjusted slightly from the logic in hstore_out. We have to take
-	 * account of out treatment of booleans to be a bit more pessimistic about
-	 * the length of values.
-	 */
+	if (HS_ISEMPTY(in))
+	{
+		out = cstring_to_text_with_len("{}",2);
+	}
+	else
+	{
+		StringInfo	str;
 
-	for (i = 0; i < count; i++)
-	{
-		/* include "" and colon-space and comma-space */
-		buflen += 6 + 2 * HS_KEYLEN(entries, i);
-		/* include "" only if nonnull */
-		buflen += 3 + (HS_VALISNULL(entries, i)
-					   ? 1
-					   : 2 * HS_VALLEN(entries, i));
-	}
-
-	out = ptr = palloc(buflen);
-
-	src = makeStringInfo();
-	dst = makeStringInfo();
-
-	*ptr++ = '{';
-
-	for (i = 0; i < count; i++)
-	{
-		resetStringInfo(src);
-		resetStringInfo(dst);
-		appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		escape_json(dst, src->data);
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
-		*ptr++ = ':';
-		*ptr++ = ' ';
-		resetStringInfo(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(src);
-			appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
+		str = makeStringInfo();
+		appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
 
-			/*
-			 * don't treat something with a leading zero followed by another
-			 * digit as numeric - could be a zip code or similar
-			 */
-			if (src->len > 0 &&
-				!(src->data[0] == '0' &&
-				  isdigit((unsigned char) src->data[1])) &&
-				strspn(src->data, "+-0123456789Ee.") == src->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(src->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;
+		HStoreToCString(str, VARDATA_ANY(in), VARSIZE_ANY(in), 
+						SET_PRETTY_PRINT_VAR(JsonOutput | RootHashDecorated | LooseOutput));
 
-					dval = strtod(src->data, &endptr);
-					(void) dval;
-					if (*endptr == '\0')
-						is_number = true;
-				}
-			}
-			if (is_number)
-				appendBinaryStringInfo(dst, src->data, src->len);
-			else
-				escape_json(dst, src->data);
-		}
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
+		out = (text*)str->data;
 
-		if (i + 1 != count)
-		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
-		}
+		SET_VARSIZE(out, str->len);
 	}
-	*ptr++ = '}';
-	*ptr = '\0';
 
-	PG_RETURN_TEXT_P(cstring_to_text(out));
+	PG_RETURN_TEXT_P(out);
 }
 
 PG_FUNCTION_INFO_V1(hstore_to_json);
@@ -1350,74 +1497,310 @@ Datum
 hstore_to_json(PG_FUNCTION_ARGS)
 {
 	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);
-	StringInfo	src,
-				dst;
+	text	   *out;
 
-	if (count == 0)
-		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
+	if (HS_ISEMPTY(in))
+	{
+		out = cstring_to_text_with_len("{}",2);
+	}
+	else
+	{
+		StringInfo	str;
 
-	buflen = 3;
+		str = makeStringInfo();
+		appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
 
-	/*
-	 * Formula adjusted slightly from the logic in hstore_out. We have to take
-	 * account of out treatment of booleans to be a bit more pessimistic about
-	 * the length of values.
-	 */
+		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;
+	HeapTuple   	tuple;
+
+	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);
+	}
+
+	return funcOid;
+}
 
-	for (i = 0; i < count; i++)
+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;
+	Oid				castOid = InvalidOid;
+	int				valueType = hsvString;
+	FmgrInfo		castInfo;
+	CoercionMethod	method;
+
+	if (ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)) == 0)
+		PG_RETURN_POINTER(hstoreDump(NULL));
+
+	switch(ARR_ELEMTYPE(array))
 	{
-		/* include "" and colon-space and comma-space */
-		buflen += 6 + 2 * HS_KEYLEN(entries, i);
-		/* include "" only if nonnull */
-		buflen += 3 + (HS_VALISNULL(entries, i)
-					   ? 1
-					   : 2 * HS_VALLEN(entries, i));
+		case BOOLOID:
+			valueType = hsvBool;
+			break;
+		case INT2OID:
+		case INT4OID:
+		case INT8OID:
+		case FLOAT4OID:
+		case FLOAT8OID:
+			castOid = searchCast(ARR_ELEMTYPE(array), NUMERICOID, &method);
+			Assert(castOid != InvalidOid);
+		case NUMERICOID:
+			valueType = hsvNumeric;
+			break;
+		default:
+			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");
+		case TEXTOID:
+			valueType = hsvString;
+			break;
 	}
 
-	out = ptr = palloc(buflen);
+	if (castOid != InvalidOid)
+		fmgr_info(castOid, &castInfo);
 
-	src = makeStringInfo();
-	dst = makeStringInfo();
+	iterator = array_create_iterator(array, 0);
 
-	*ptr++ = '{';
+	value.type = hsvArray;
+	value.array.scalar = false;
+	for(i=0; i<ncounters; i++)
+	{
+		value.array.nelems = dims[i];
+		result = pushHStoreValue(&state, WHS_BEGIN_ARRAY, &value);
+	}
 
-	for (i = 0; i < count; i++)
+	while(array_iterate(iterator, &datum, &isnull))
 	{
-		resetStringInfo(src);
-		resetStringInfo(dst);
-		appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		escape_json(dst, src->data);
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
-		*ptr++ = ':';
-		*ptr++ = ' ';
-		resetStringInfo(dst);
-		if (HS_VALISNULL(entries, i))
-			appendStringInfoString(dst, "null");
+		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);
+			}
+		}
 		else
 		{
-			resetStringInfo(src);
-			appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
-			escape_json(dst, src->data);
+			counters[i]++;
 		}
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
 
-		if (i + 1 != count)
+		if (isnull)
+		{
+			value.type = hsvNull;
+			value.size = sizeof(HEntry);
+		}
+		else
 		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
+			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, "Impossible state: %d", valueType);
+			}
 		}
+
+		result = pushHStoreValue(&state, WHS_ELEM, &value);
+	}
+
+	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);
+}
+
+PG_FUNCTION_INFO_V1(hstore2jsonb);
+Datum		hstore2jsonb(PG_FUNCTION_ARGS);
+Datum
+hstore2jsonb(PG_FUNCTION_ARGS)
+{
+	HStore	*hs = PG_GETARG_HS(0);
+	Jsonb	*jb = palloc(VARSIZE_ANY(hs));
+
+	memcpy(jb, hs, VARSIZE_ANY(hs));
+
+	if (VARSIZE_ANY_EXHDR(jb) >= sizeof(uint32))
+	{
+		uint32 *header = (uint32*)VARDATA_ANY(jb);
+
+		*header &= ~JB_FLAG_UNUSED;
+	}
+
+	PG_RETURN_JSONB(jb);
+}
+
+PG_FUNCTION_INFO_V1(jsonb2hstore);
+Datum		jsonb2hstore(PG_FUNCTION_ARGS);
+Datum
+jsonb2hstore(PG_FUNCTION_ARGS)
+{
+	Jsonb	*jb = PG_GETARG_JSONB(0);
+	HStore	*hs = palloc(VARSIZE_ANY(jb));
+
+	memcpy(hs, jb, VARSIZE_ANY(jb));
+
+	if (VARSIZE_ANY_EXHDR(hs) >= sizeof(uint32))
+	{
+		uint32	*header = (uint32*)VARDATA_ANY(hs);
+
+		*header |= HS_FLAG_NEWVERSION;
+	}
+
+	PG_RETURN_POINTER(hs);
+}
+
+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);
+
+	if (l > sizeof(uint32))
+	{
+		uint32	*header = (uint32*)buffer;
+
+		*header |= HS_FLAG_NEWVERSION;
 	}
-	*ptr++ = '}';
-	*ptr = '\0';
 
-	PG_RETURN_TEXT_P(cstring_to_text(out));
+	return l;
 }
+
+
+
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 45edb04..06cc450 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -25,154 +25,597 @@ 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;
 
-/*
- * 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)
+	deconstruct_array(a,
+					  TEXTOID, -1, false, 'i',
+					  &key_datums, &key_nulls, &key_count);
+
+	if (key_count == 0)
+		return NULL;
+
+	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)
 {
-	HEntry	   *entries = ARRPTR(hs);
-	int			stopLow = lowbound ? *lowbound : 0;
-	int			stopHigh = HS_COUNT(hs);
-	int			stopMiddle;
-	char	   *base = STRPTR(hs);
+	HStoreValue		*stopLow = a->array.elems + ((lowbound) ? *lowbound : 0),
+					*stopHigh = a->array.elems + a->array.nelems,
+					*stopMiddle;
 
 	while (stopLow < stopHigh)
 	{
-		int			difference;
+		int diff;
 
 		stopMiddle = stopLow + (stopHigh - stopLow) / 2;
 
-		if (HS_KEYLEN(entries, stopMiddle) == keylen)
-			difference = memcmp(HS_KEY(entries, base, stopMiddle), key, keylen);
+		if (keylen == stopMiddle->string.len)
+			diff = memcmp(stopMiddle->string.val, key, keylen);
 		else
-			difference = (HS_KEYLEN(entries, stopMiddle) > keylen) ? 1 : -1;
+			diff = (stopMiddle->string.len > keylen) ? 1 : -1;
 
-		if (difference == 0)
+		if (diff == 0)
 		{
 			if (lowbound)
-				*lowbound = stopMiddle + 1;
+				*lowbound = (stopMiddle - a->array.elems) + 1;
 			return stopMiddle;
 		}
-		else if (difference < 0)
+		else if (diff < 0)
+		{
 			stopLow = stopMiddle + 1;
+		}
 		else
+		{
 			stopHigh = stopMiddle;
+		}
 	}
 
 	if (lowbound)
-		*lowbound = stopLow;
-	return -1;
+		*lowbound = (stopLow - a->array.elems) + 1;
+
+	return NULL;
 }
 
-Pairs *
-hstoreArrayToPairs(ArrayType *a, int *npairs)
+PG_FUNCTION_INFO_V1(hstore_fetchval);
+Datum		hstore_fetchval(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval(PG_FUNCTION_ARGS)
 {
-	Datum	   *key_datums;
-	bool	   *key_nulls;
-	int			key_count;
-	Pairs	   *key_pairs;
-	int			bufsiz;
-	int			i,
-				j;
+	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);
+}
 
-	deconstruct_array(a,
-					  TEXTOID, -1, false, 'i',
-					  &key_datums, &key_nulls, &key_count);
+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));
 
-	if (key_count == 0)
+		memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+		PG_RETURN_NUMERIC(out);
+	}
+
+	PG_RETURN_NULL();
+}
+
+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)
 	{
-		*npairs = 0;
-		return NULL;
+		Numeric		out = palloc(VARSIZE_ANY(v->numeric));
+
+		memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+		PG_RETURN_NUMERIC(out);
 	}
 
-	key_pairs = palloc(sizeof(Pairs) * key_count);
+	PG_RETURN_NULL();
+}
 
-	for (i = 0, j = 0; i < key_count; i++)
+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)
+{
+	bool	negative = false;
+	char 	*p = c;
+
+	*acc = 0;
+
+	while(isspace(*p) && p - c < l)
+		p++;
+
+	if (p - c >= l)
+		return false;
+
+	if (*p == '-')
 	{
-		if (!key_nulls[i])
+		negative = true;
+		p++;
+	}
+	else if (*p == '+')
+	{
+		p++;
+	}
+
+	if (p - c >= l)
+		return false;
+
+
+	while(p - c < l)
+	{
+		if (!isdigit(*p))
+			return false;
+
+		*acc *= 10;
+		*acc += (*p - '0');
+		p++;
+	}
+
+	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)
+		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;
+
+	if (path_len == 0)
+		return v;
+
+	for(i=0; v != NULL && i<path_len; i++)
+	{
+		uint32	header;
+
+		if (v->type != hsvBinary || path_nulls[i])
+			return NULL;
+
+		header = *(uint32*)v->binary.data;
+
+		if (header & HS_FLAG_HASH)
 		{
-			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++;
+			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
+		{
+			elog(PANIC,"wrong header type: %08x", header);
 		}
 	}
 
-	*npairs = hstoreUniquePairs(key_pairs, j, &bufsiz);
+	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;
 
-	return key_pairs;
+	if ((out = HStoreValueToText(hstoreDeepFetch(hs, path))) == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_TEXT_P(out);
 }
 
+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);
 
-PG_FUNCTION_INFO_V1(hstore_fetchval);
-Datum		hstore_fetchval(PG_FUNCTION_ARGS);
+	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);
 Datum
-hstore_fetchval(PG_FUNCTION_ARGS)
+hstore_fetchval_path_boolean(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	HEntry	   *entries = ARRPTR(hs);
-	text	   *out;
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
+	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			*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);
 
-	if (idx < 0 || HS_VALISNULL(entries, idx))
+		Assert(v->type == hsvBinary);
+		SET_VARSIZE(out, VARHDRSZ + v->binary.len);
+		memcpy(VARDATA(out), v->binary.data, v->binary.len);
+	}
+
+	return out;
+}
+
+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)
 		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;
 
-	out = cstring_to_text_with_len(HS_VAL(entries, STRPTR(hs), idx),
-								   HS_VALLEN(entries, idx));
+	if (!HS_ISEMPTY(hs))
+		v = getHStoreValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, i);
 
-	PG_RETURN_TEXT_P(out);
+	if ((out = HStoreValueToHStore(v)) == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_POINTER(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);
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
+	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_RETURN_BOOL(idx >= 0);
+
+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);
+
+	PG_RETURN_BOOL(hstoreDeepFetch(hs, path) != NULL);
 }
 
 
+
 PG_FUNCTION_INFO_V1(hstore_exists_any);
 Datum		hstore_exists_any(PG_FUNCTION_ARGS);
 Datum
 hstore_exists_any(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(keys, &nkeys);
-	int			i;
-	int			lowbound = 0;
-	bool		res = false;
-
+	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;
 	/*
 	 * we exploit the fact that the pairs list is already sorted into strictly
-	 * increasing order to narrow the hstoreFindKey search; each search can
+	 * 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.
 	 */
-	for (i = 0; i < nkeys; i++)
+	for (i = 0; i < v->array.nelems; i++)
 	{
-		int			idx = hstoreFindKey(hs, &lowbound,
-									  key_pairs[i].key, key_pairs[i].keylen);
-
-		if (idx >= 0)
+		if (findUncompressedHStoreValueByValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, plowbound,
+											   v->array.elems + i) != NULL)
 		{
 			res = true;
 			break;
@@ -188,26 +631,36 @@ Datum		hstore_exists_all(PG_FUNCTION_ARGS);
 Datum
 hstore_exists_all(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(keys, &nkeys);
-	int			i;
-	int			lowbound = 0;
-	bool		res = true;
+	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);
+	}
 
+	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 hstoreFindKey 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 findUncompressedHStoreValue search;
+	 * each search can start one entry past the previous "found" entry,
+	 * or at the lower bound of the last search.
 	 */
-	for (i = 0; i < nkeys; i++)
+	for (i = 0; i < v->array.nelems; i++)
 	{
-		int			idx = hstoreFindKey(hs, &lowbound,
-									  key_pairs[i].key, key_pairs[i].keylen);
-
-		if (idx < 0)
+		if (findUncompressedHStoreValueByValue(VARDATA(hs),
+											   HS_FLAG_HASH | HS_FLAG_ARRAY,
+											   plowbound,
+											   v->array.elems + i) == NULL)
 		{
 			res = false;
 			break;
@@ -223,14 +676,18 @@ Datum		hstore_defined(PG_FUNCTION_ARGS);
 Datum
 hstore_defined(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	HEntry	   *entries = ARRPTR(hs);
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
-	bool		res = (idx >= 0 && !HS_VALISNULL(entries, idx));
-
-	PG_RETURN_BOOL(res);
+	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));
 }
 
 
@@ -239,483 +696,1336 @@ Datum		hstore_delete(PG_FUNCTION_ARGS);
 Datum
 hstore_delete(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	char	   *keyptr = VARDATA_ANY(key);
-	int			keylen = VARSIZE_ANY_EXHDR(key);
-	HStore	   *out = palloc(VARSIZE(hs));
-	char	   *bufs,
-			   *bufd,
-			   *ptrd;
-	HEntry	   *es,
-			   *ed;
-	int			i;
-	int			count = HS_COUNT(hs);
-	int			outcount = 0;
-
-	SET_VARSIZE(out, VARSIZE(hs));
-	HS_SETCOUNT(out, count);	/* temporary! */
+	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);
 
-	bufs = STRPTR(hs);
-	es = ARRPTR(hs);
-	bufd = ptrd = STRPTR(out);
-	ed = ARRPTR(out);
+	it = HStoreIteratorInit(VARDATA(in));
 
-	for (i = 0; i < count; ++i)
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		int			len = HS_KEYLEN(es, i);
-		char	   *ptrs = HS_KEY(es, bufs, i);
+		skipNested = true;
 
-		if (!(len == keylen && memcmp(ptrs, keyptr, keylen) == 0))
+		if ((r == WHS_ELEM || r == WHS_KEY) &&
+			(v.type == hsvString && keylen == v.string.len &&
+			 memcmp(keyptr, v.string.val, keylen) == 0))
 		{
-			int			vallen = HS_VALLEN(es, i);
+			if (r == WHS_KEY)
+				/* skip corresponding value */
+				HStoreIteratorGet(&it, &v, true);
 
-			HS_COPYITEM(ed, bufd, ptrd, ptrs, len, vallen, HS_VALISNULL(es, i));
-			++outcount;
+			continue;
 		}
+
+		res = pushHStoreValue(&toState, r, &v);
 	}
 
-	HS_FINALIZE(out, outcount, bufd, ptrd);
+	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);
+	}
 
 	PG_RETURN_POINTER(out);
 }
 
-
 PG_FUNCTION_INFO_V1(hstore_delete_array);
 Datum		hstore_delete_array(PG_FUNCTION_ARGS);
 Datum
 hstore_delete_array(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HStore	   *out = palloc(VARSIZE(hs));
-	int			hs_count = HS_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)
+	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)
 	{
-		/* return a copy of the input, unchanged */
-		memcpy(out, hs, VARSIZE(hs));
-		HS_FIXSIZE(out, hs_count);
-		HS_SETCOUNT(out, hs_count);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	/*
-	 * this is in effect a merge between hs and key_pairs, both of which are
-	 * already sorted by (keylen,key); we take keys from hs only
-	 */
+	it = HStoreIteratorInit(VARDATA(in));
 
-	for (i = j = 0; i < hs_count;)
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		int			difference;
 
-		if (j >= nkeys)
-			difference = -1;
-		else
+		if (skipNested == false)
 		{
-			int			skeylen = HS_KEYLEN(es, i);
-
-			if (skeylen == key_pairs[j].keylen)
-				difference = memcmp(HS_KEY(es, ps, i),
-									key_pairs[j].key,
-									key_pairs[j].keylen);
-			else
-				difference = (skeylen > key_pairs[j].keylen) ? 1 : -1;
+			Assert(v.type == hsvArray || v.type == hsvHash);
+			isHash = (v.type == hsvArray) ? false : true;
+			skipNested = true;
 		}
 
-		if (difference > 0)
-			++j;
-		else if (difference == 0)
-			++i, ++j;
-		else
+		if ((r == WHS_ELEM || r == WHS_KEY) && v.type == hsvString &&
+			i < a->array.nelems)
 		{
-			HS_COPYITEM(ed, bufd, pd,
-						HS_KEY(es, ps, i), HS_KEYLEN(es, i),
-						HS_VALLEN(es, i), HS_VALISNULL(es, i));
-			++outcount;
-			++i;
-		}
-	}
+			int diff;
 
-	HS_FINALIZE(out, outcount, bufd, pd);
+			if (isHash)
+			{
+				do {
+					diff = compareHStoreStringValue(&v, a->array.elems + i,
+													NULL);
 
-	PG_RETURN_POINTER(out);
-}
+					if (diff >= 0)
+						i++;
+				} while(diff > 0 && i < a->array.nelems);
+			}
+			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);
 
-PG_FUNCTION_INFO_V1(hstore_delete_hstore);
-Datum		hstore_delete_hstore(PG_FUNCTION_ARGS);
-Datum
-hstore_delete_hstore(PG_FUNCTION_ARGS)
+				continue;
+			}
+		}
+
+		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);
+	}
+
+	PG_RETURN_POINTER(out);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_delete_hstore);
+Datum		hstore_delete_hstore(PG_FUNCTION_ARGS);
+Datum
+hstore_delete_hstore(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HStore	   *hs2 = PG_GETARG_HS(1);
-	HStore	   *out = palloc(VARSIZE(hs));
-	int			hs_count = HS_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);
+	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));
 		PG_RETURN_POINTER(out);
 	}
 
-	/*
-	 * this is in effect a merge between hs and hs2, both of which are already
-	 * sorted by (keylen,key); we take keys from hs only; for equal keys, we
-	 * take the value from hs unless the values are equal
-	 */
+	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);
 
-	for (i = j = 0; i < hs_count;)
+	if (isHash1 == true && isHash2 == true)
 	{
-		int			difference;
+		bool			fin2 = false,
+						keyIsDef = false;
 
-		if (j >= hs2_count)
-			difference = -1;
-		else
+		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)
 		{
-			int			skeylen = HS_KEYLEN(es, i);
-			int			s2keylen = HS_KEYLEN(es2, j);
 
-			if (skeylen == s2keylen)
-				difference = memcmp(HS_KEY(es, ps, i),
-									HS_KEY(es2, ps2, j),
-									skeylen);
+			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);
+
+	if (r == WHS_BEGIN_ARRAY)
+	{
+		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
-				difference = (skeylen > s2keylen) ? 1 : -1;
+				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;
 		}
 
-		if (difference > 0)
-			++j;
-		else if (difference == 0)
+		if (level == path_len - 1)
 		{
-			int			svallen = HS_VALLEN(es, i);
-			int			snullval = HS_VALISNULL(es, i);
+			/* last level in path, skip all elem */
+			r = HStoreIteratorGet(it, &v, true);
+			Assert(r == WHS_ELEM);
+		}
+		else
+		{
+			res = deletePathDo(it, path_elems, path_nulls, path_len, st,
+							   level + 1);
+		}
 
-			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)))
+		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)
 			{
-				HS_COPYITEM(ed, bufd, pd,
-							HS_KEY(es, ps, i), HS_KEYLEN(es, i),
-							svallen, snullval);
-				++outcount;
+				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);
+		}
+
+		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);
+
+			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)
+			{
+				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;
 			}
-			++i, ++j;
+
+			pushHStoreValue(st, r, &k);
+			r = HStoreIteratorGet(it, &v, true);
+			Assert(r == WHS_VALUE);
+			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) /* just a string or null */
+	{
+		pushHStoreValue(st, r, &v);
+		res = (void*)0x01; /* dummy value */
+	}
+	else
+	{
+		elog(PANIC, "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)
 		{
-			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 (i++ == idx)
+			{
+				if (r == WHS_KEY)
+					HStoreIteratorGet(&it, &v, true); /* skip value */
+				continue;
+			}
 		}
+
+		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(PANIC,"Could not convert to string");
+	}
+}
+
+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
+	{
+		int 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(PANIC, "impossible state");
+	}
 
-	PG_RETURN_POINTER(out);
+	return res;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_concat);
-Datum		hstore_concat(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_replace);
+Datum		hstore_replace(PG_FUNCTION_ARGS);
 Datum
-hstore_concat(PG_FUNCTION_ARGS)
+hstore_replace(PG_FUNCTION_ARGS)
 {
-	HStore	   *s1 = PG_GETARG_HS(0);
-	HStore	   *s2 = PG_GETARG_HS(1);
-	HStore	   *out = palloc(VARSIZE(s1) + VARSIZE(s2));
-	char	   *ps1,
-			   *ps2,
-			   *bufd,
-			   *pd;
-	HEntry	   *es1,
-			   *es2,
-			   *ed;
-	int			s1idx;
-	int			s2idx;
-	int			s1count = 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);
+	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));
 		PG_RETURN_POINTER(out);
 	}
 
-	if (s2count == 0)
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &path_elems, &path_nulls, &path_len);
+
+	if (path_len == 0)
 	{
-		/* return a copy of the input, unchanged */
-		memcpy(out, s1, VARSIZE(s1));
-		HS_FIXSIZE(out, s1count);
-		HS_SETCOUNT(out, s1count);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	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)
+	if (HS_ROOT_COUNT(newval) == 0)
+	{
+		value.type = hsvNull;
+		value.size = sizeof(HEntry);
+	}
+	else
 	{
-		int			difference;
+		value.type = hsvBinary;
+		value.binary.data = VARDATA(newval);
+		value.binary.len = VARSIZE_ANY_EXHDR(newval);
+		value.size = value.binary.len + sizeof(HEntry);
+	}
 
-		if (s1idx >= s1count)
-			difference = 1;
-		else if (s2idx >= s2count)
-			difference = -1;
-		else
-		{
-			int			s1keylen = HS_KEYLEN(es1, s1idx);
-			int			s2keylen = HS_KEYLEN(es2, s2idx);
+	it = HStoreIteratorInit(VARDATA(in));
 
-			if (s1keylen == s2keylen)
-				difference = memcmp(HS_KEY(es1, ps1, s1idx),
-									HS_KEY(es2, ps2, s2idx),
-									s1keylen);
-			else
-				difference = (s1keylen > s2keylen) ? 1 : -1;
-		}
+	res = replacePathDo(&it, path_elems, path_nulls, path_len, &st, 0, &value);
 
-		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;
-		}
+	if (res == NULL)
+	{
+		SET_VARSIZE(out, VARHDRSZ);
 	}
+	else
+	{
+		int				sz;
 
-	HS_FINALIZE(out, outcount, bufd, pd);
+		sz = compressHStore(res, VARDATA(out));
+		SET_VARSIZE(out, sz + 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)
+static HStoreValue*
+concatPathDo(HStoreIterator **it, Datum *path_elems,
+			 bool *path_nulls, int path_len,
+			 ToHStoreState  **st, int level, HStoreIterator	*toConcat)
 {
-	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;
+	HStoreValue v, *res = NULL;
+	int			r;
 
-	deconstruct_array(key_array,
-					  TEXTOID, -1, false, 'i',
-					  &key_datums, &key_nulls, &key_count);
+	r = HStoreIteratorGet(it, &v, false);
 
-	if (key_count == 0)
+	if (r == WHS_BEGIN_ARRAY)
 	{
-		aout = construct_empty_array(TEXTOID);
-		PG_RETURN_POINTER(aout);
-	}
+		int		idx, i;
+		uint32	n = v.array.nelems;
 
-	out_datums = palloc(sizeof(Datum) * key_count);
-	out_nulls = palloc(sizeof(bool) * key_count);
+		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;
+		}
 
-	for (i = 0; i < key_count; ++i)
-	{
-		text	   *key = (text *) DatumGetPointer(key_datums[i]);
-		int			idx;
+		if (idx > n)
+			idx = n;
 
-		if (key_nulls[i])
-			idx = -1;
-		else
-			idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ);
+		pushHStoreValue(st, r, &v);
 
-		if (idx < 0 || HS_VALISNULL(entries, idx))
+		for(i=0; i<n; i++)
 		{
-			out_nulls[i] = true;
-			out_datums[i] = (Datum) 0;
+			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);
+			}
 		}
-		else
+
+		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++)
 		{
-			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, &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);
+			}
 		}
-	}
 
-	aout = construct_md_array(out_datums, out_nulls,
-							  ARR_NDIM(key_array),
-							  ARR_DIMS(key_array),
-							  ARR_LBOUND(key_array),
-							  TEXTOID, -1, false, 'i');
+		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(PANIC, "impossible state");
+	}
 
-	PG_RETURN_POINTER(aout);
+	return res;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_slice_to_hstore);
-Datum		hstore_slice_to_hstore(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_deep_concat);
+Datum		hstore_deep_concat(PG_FUNCTION_ARGS);
 Datum
-hstore_slice_to_hstore(PG_FUNCTION_ARGS)
+hstore_deep_concat(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *ptr = STRPTR(hs);
-	ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(1);
-	HStore	   *out;
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(key_array, &nkeys);
-	Pairs	   *out_pairs;
-	int			bufsiz;
-	int			lastidx = 0;
-	int			i;
-	int			out_count = 0;
-
-	if (nkeys == 0)
+	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")));
+
+	if (HS_ROOT_COUNT(in) == 0 || HS_ROOT_COUNT(newval) == 0)
 	{
-		out = hstorePairs(NULL, 0, 0);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	out_pairs = palloc(sizeof(Pairs) * nkeys);
-	bufsiz = 0;
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &path_elems, &path_nulls, &path_len);
 
-	/*
-	 * 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.
-	 */
+	it1 = HStoreIteratorInit(VARDATA(in));
+	it2 = HStoreIteratorInit(VARDATA(newval));
 
-	for (i = 0; i < nkeys; ++i)
-	{
-		int			idx = hstoreFindKey(hs, &lastidx,
-									  key_pairs[i].key, key_pairs[i].keylen);
+	if (path_len == 0)
+		res = IteratorConcat(&it1, &it2, &st);
+	else
+		res = concatPathDo(&it1, path_elems, path_nulls, path_len, &st, 0, it2);
 
-		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;
-		}
+	if (res == NULL)
+	{
+		SET_VARSIZE(out, VARHDRSZ);
 	}
+	else
+	{
+		int				sz;
 
-	/*
-	 * we don't use uniquePairs here because we know that the pairs list is
-	 * already sorted and uniq'ed.
-	 */
-
-	out = hstorePairs(out_pairs, out_count, bufsiz);
+		sz = compressHStore(res, VARDATA(out));
+		SET_VARSIZE(out, sz + VARHDRSZ);
+	}
 
 	PG_RETURN_POINTER(out);
 }
 
-
 PG_FUNCTION_INFO_V1(hstore_akeys);
 Datum		hstore_akeys(PG_FUNCTION_ARGS);
 Datum
 hstore_akeys(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	Datum	   *d;
-	ArrayType  *a;
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *base = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			i;
-
-	if (count == 0)
+	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))
 	{
 		a = construct_empty_array(TEXTOID);
 		PG_RETURN_POINTER(a);
 	}
 
-	d = (Datum *) palloc(sizeof(Datum) * count);
+	d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
 
-	for (i = 0; i < count; ++i)
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		text	   *item = cstring_to_text_with_len(HS_KEY(entries, base, i),
-													HS_KEYLEN(entries, i));
+		skipNested = true;
 
-		d[i] = PointerGetDatum(item);
+		if ((r == WHS_ELEM && v.type != hsvNull) || r == WHS_KEY)
+			d[i++] = PointerGetDatum(HStoreValueToText(&v));
 	}
 
-	a = construct_array(d, count,
+	a = construct_array(d, i,
 						TEXTOID, -1, false, 'i');
 
 	PG_RETURN_POINTER(a);
@@ -727,43 +2037,40 @@ Datum		hstore_avals(PG_FUNCTION_ARGS);
 Datum
 hstore_avals(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	Datum	   *d;
-	bool	   *nulls;
-	ArrayType  *a;
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *base = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			lb = 1;
-	int			i;
-
-	if (count == 0)
+	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))
 	{
 		a = construct_empty_array(TEXTOID);
 		PG_RETURN_POINTER(a);
 	}
 
-	d = (Datum *) palloc(sizeof(Datum) * count);
-	nulls = (bool *) palloc(sizeof(bool) * count);
+	d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
+	nulls = (bool *) palloc(sizeof(bool) * HS_ROOT_COUNT(hs));
 
-	for (i = 0; i < count; ++i)
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		if (HS_VALISNULL(entries, i))
-		{
-			d[i] = (Datum) 0;
-			nulls[i] = true;
-		}
-		else
-		{
-			text	   *item = cstring_to_text_with_len(HS_VAL(entries, base, i),
-													  HS_VALLEN(entries, i));
+		skipNested = true;
 
-			d[i] = PointerGetDatum(item);
-			nulls[i] = false;
+		if (r == WHS_ELEM || r == WHS_VALUE)
+		{
+			d[i] = PointerGetDatum(HStoreValueToText(&v));
+			nulls[i] = (DatumGetPointer(d[i]) == NULL) ? true : false;
+			i++;
 		}
 	}
 
-	a = construct_md_array(d, nulls, 1, &count, &lb,
+	a = construct_md_array(d, nulls, 1, &i, &lb,
 						   TEXTOID, -1, false, 'i');
 
 	PG_RETURN_POINTER(a);
@@ -773,44 +2080,53 @@ hstore_avals(PG_FUNCTION_ARGS)
 static ArrayType *
 hstore_to_array_internal(HStore *hs, int ndims)
 {
-	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;
+	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;
 
 	Assert(ndims < 3);
 
 	if (count == 0 || ndims == 0)
 		return construct_empty_array(TEXTOID);
 
-	out_size[0] = count * 2 / ndims;
+	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_datums = palloc(sizeof(Datum) * count * 2);
 	out_nulls = palloc(sizeof(bool) * count * 2);
 
-	for (i = 0; i < count; ++i)
-	{
-		text	   *key = cstring_to_text_with_len(HS_KEY(entries, base, i),
-												   HS_KEYLEN(entries, i));
+	it = HStoreIteratorInit(VARDATA(hs));
 
-		out_datums[i * 2] = PointerGetDatum(key);
-		out_nulls[i * 2] = false;
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
 
-		if (HS_VALISNULL(entries, i))
-		{
-			out_datums[i * 2 + 1] = (Datum) 0;
-			out_nulls[i * 2 + 1] = true;
-		}
-		else
+		switch(r)
 		{
-			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;
+			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;
 		}
 	}
 
@@ -850,222 +2166,526 @@ hstore_to_matrix(PG_FUNCTION_ARGS)
  * there was no explanatory comment in the original code. --AG)
  */
 
-static void
-setup_firstcall(FuncCallContext *funcctx, HStore *hs,
-				FunctionCallInfoData *fcinfo)
-{
-	MemoryContext oldcontext;
-	HStore	   *st;
+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;
+	}
+
+	while(st->level >= 0)
+	{
+		uint32	header;
 
-	oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+		v = NULL;
+		if (st->path[st->level].v.type != hsvBinary)
+		{
+			st->level--;
+			continue;
+		}
 
-	st = (HStore *) palloc(VARSIZE(hs));
-	memcpy(st, hs, VARSIZE(hs));
+		header = *(uint32*)st->path[st->level].v.binary.data;
 
-	funcctx->user_fctx = (void *) st;
+		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;
 
-	if (fcinfo)
-	{
-		TupleDesc	tupdesc;
+				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;
+					}
+				}
 
-		/* 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");
+				v = getHStoreValue(st->path[st->level].v.binary.data,
+								   HS_FLAG_ARRAY, ith);
+			}
+			else
+			{
+				st->level--;
+				continue;
+			}
+		}
+		else
+		{
+			elog(PANIC, "impossible state");
+		}
 
-		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+		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;
+		}
 	}
 
-	MemoryContextSwitchTo(oldcontext);
+	return v;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_skeys);
-Datum		hstore_skeys(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_svals_path);
+Datum		hstore_svals_path(PG_FUNCTION_ARGS);
 Datum
-hstore_skeys(PG_FUNCTION_ARGS)
+hstore_svals_path(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	HStoreValue			*v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, NULL);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	if ((v = getNextValsPath(st)) != NULL)
 	{
-		HEntry	   *entries = ARRPTR(hs);
-		text	   *item;
+		text	*item = HStoreValueToText(v);
 
-		item = cstring_to_text_with_len(HS_KEY(entries, STRPTR(hs), i),
-										HS_KEYLEN(entries, i));
-
-		SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
+		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);
+PG_FUNCTION_INFO_V1(hstore_hvals_path);
+Datum		hstore_hvals_path(PG_FUNCTION_ARGS);
 Datum
-hstore_svals(PG_FUNCTION_ARGS)
+hstore_hvals_path(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	HStoreValue 		*v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, NULL);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0),
+							 PG_GETARG_ARRAYTYPE_P(1), NULL);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	if ((v = getNextValsPath(st)) != NULL)
 	{
-		HEntry	   *entries = ARRPTR(hs);
+		HStore	   *item = HStoreValueToHStore(v);
 
-		if (HS_VALISNULL(entries, i))
-		{
-			ReturnSetInfo *rsi;
-
-			/* ugly ugly ugly. why no macro for this? */
-			(funcctx)->call_cntr++;
-			rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-			rsi->isDone = ExprMultipleResult;
-			PG_RETURN_NULL();
-		}
+		if (item == NULL)
+			SRF_RETURN_NEXT_NULL(funcctx);
 		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_contains);
-Datum		hstore_contains(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_each);
+Datum		hstore_each(PG_FUNCTION_ARGS);
 Datum
-hstore_contains(PG_FUNCTION_ARGS)
+hstore_each(PG_FUNCTION_ARGS)
 {
-	HStore	   *val = PG_GETARG_HS(0);
-	HStore	   *tmpl = PG_GETARG_HS(1);
-	bool		res = true;
-	HEntry	   *te = ARRPTR(tmpl);
-	char	   *tstr = STRPTR(tmpl);
-	HEntry	   *ve = ARRPTR(val);
-	char	   *vstr = STRPTR(val);
-	int			tcount = HS_COUNT(tmpl);
-	int			lastidx = 0;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	int					r;
+	HStoreValue			v;
 
-	/*
-	 * 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
-	 */
+	if (SRF_IS_FIRSTCALL())
+	{
+		funcctx = SRF_FIRSTCALL_INIT();
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	for (i = 0; res && i < tcount; ++i)
+	while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
 	{
-		int			idx = hstoreFindKey(val, &lastidx,
-									  HS_KEY(te, tstr, i), HS_KEYLEN(te, i));
+		Datum		res,
+					dvalues[2] = {0, 0};
+		bool		nulls[2] = {false, false};
+		text	   *item;
+		HeapTuple	tuple;
 
-		if (idx >= 0)
+		if (r == WHS_ELEM)
 		{
-			bool		nullval = HS_VALISNULL(te, i);
-			int			vallen = HS_VALLEN(te, i);
+			nulls[0] = true;
 
-			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;
+			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);
 		}
 		else
-			res = false;
-	}
+		{
+			continue;
+		}
 
-	PG_RETURN_BOOL(res);
-}
+		tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
+		res = HeapTupleGetDatum(tuple);
 
+		SRF_RETURN_NEXT(funcctx, PointerGetDatum(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)
-										));
+	SRF_RETURN_DONE(funcctx);
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_each);
-Datum		hstore_each(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_each_hstore);
+Datum		hstore_each_hstore(PG_FUNCTION_ARGS);
 Datum
-hstore_each(PG_FUNCTION_ARGS)
+hstore_each_hstore(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	int					r;
+	HStoreValue			v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, fcinfo);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
 	{
-		HEntry	   *entries = ARRPTR(hs);
-		char	   *ptr = STRPTR(hs);
 		Datum		res,
-					dvalues[2];
+					dvalues[2] = {0, 0};
 		bool		nulls[2] = {false, false};
 		text	   *item;
+		HStore		*hitem;
 		HeapTuple	tuple;
 
-		item = cstring_to_text_with_len(HS_KEY(entries, ptr, i),
-										HS_KEYLEN(entries, i));
-		dvalues[0] = PointerGetDatum(item);
+		if (r == WHS_ELEM)
+		{
+			nulls[0] = true;
 
-		if (HS_VALISNULL(entries, i))
+			hitem = HStoreValueToHStore(&v);
+			if (hitem == NULL)
+				nulls[1] = true;
+			else
+				dvalues[1] = PointerGetDatum(hitem);
+		}
+		else if (r == WHS_KEY)
 		{
-			dvalues[1] = (Datum) 0;
-			nulls[1] = true;
+			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);
 		}
 		else
 		{
-			item = cstring_to_text_with_len(HS_VAL(entries, ptr, i),
-											HS_VALLEN(entries, i));
-			dvalues[1] = PointerGetDatum(item);
+			continue;
 		}
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
@@ -1077,6 +2697,183 @@ hstore_each(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(PANIC, "impossible state");
+	}
+
+	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
@@ -1089,72 +2886,28 @@ Datum		hstore_cmp(PG_FUNCTION_ARGS);
 Datum
 hstore_cmp(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs1 = PG_GETARG_HS(0);
-	HStore	   *hs2 = PG_GETARG_HS(1);
-	int			hcount1 = HS_COUNT(hs1);
-	int			hcount2 = HS_COUNT(hs2);
-	int			res = 0;
+	HStore	   		*hs1 = PG_GETARG_HS(0);
+	HStore	   		*hs2 = PG_GETARG_HS(1);
+	int				res;
 
-	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) || HS_ISEMPTY(hs2))
 	{
-		/* 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(hs1))
 		{
-			if (len1 > len2)
-				res = 1;
-			else if (len1 < len2)
-				res = -1;
-			else if (hcount1 > hcount2)
-				res = 1;
-			else if (hcount2 > hcount1)
-				res = -1;
+			if (HS_ISEMPTY(hs2))
+				res = 0;
 			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;
-				}
-			}
+				res = -1;
 		}
 		else
 		{
-			res = (res > 0) ? 1 : -1;
+			res = 1;
 		}
 	}
+	else
+	{
+		res = compareHStoreBinaryValue(VARDATA(hs1), VARDATA(hs2));
+	}
 
 	/*
 	 * this is a btree support function; this is one of the few places where
@@ -1248,17 +3001,61 @@ 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
new file mode 100644
index 0000000..3a357e1
--- /dev/null
+++ b/contrib/hstore/hstore_scan.l
@@ -0,0 +1,311 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 (JsonbStringIsNumber(scanstring.val, scanstring.len))
+	{
+		/* 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;
+}
+
diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql
index 9518f56..10afc45 100644
--- a/contrib/hstore/sql/hstore.sql
+++ b/contrib/hstore/sql/hstore.sql
@@ -39,6 +39,9 @@ select 'aa=>"bb" ,cc=>dd'::hstore;
 select 'aa=>null'::hstore;
 select 'aa=>NuLl'::hstore;
 select 'aa=>"NuLl"'::hstore;
+select 'aa=>nul'::hstore;
+select 'aa=>NuL'::hstore;
+select 'aa=>"NuL"'::hstore;
 
 select e'\\=a=>q=w'::hstore;
 select e'"=a"=>q\\=w'::hstore;
@@ -213,7 +216,7 @@ select hstore(v) from testhstore1 v;
 select hstore(null::testhstore0);
 select hstore(null::testhstore1);
 select pg_column_size(hstore(v))
-         = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore)
+         = pg_column_size('a=>"1", b=>"foo", c=>1.2, d=>"3", e=>"0"'::hstore)
   from testhstore1 v;
 select populate_record(v, hstore('c', '3.45')) from testhstore1 v;
 select populate_record(v, hstore('d', '3.45')) from testhstore1 v;
@@ -299,6 +302,8 @@ select count(*) from testhstore where h ? 'public';
 select count(*) from testhstore where h ?| ARRAY['public','disabled'];
 select count(*) from testhstore where h ?& ARRAY['public','disabled'];
 
+RESET enable_seqscan;
+
 drop index hidx;
 create index hidx on testhstore using gin (h);
 set enable_seqscan=off;
@@ -310,6 +315,8 @@ select count(*) from testhstore where h ? 'public';
 select count(*) from testhstore where h ?| ARRAY['public','disabled'];
 select count(*) from testhstore where h ?& ARRAY['public','disabled'];
 
+RESET enable_seqscan;
+
 select count(*) from (select (each(h)).key from testhstore) as wow ;
 select key, count(*) from (select (each(h)).key from testhstore) as wow group by key order by count desc, key;
 
@@ -323,6 +330,9 @@ select count(*) from (select h from (select * from testhstore union all select *
 select distinct * from (values (hstore '' || ''),('')) v(h);
 set enable_sort = true;
 
+RESET enable_hashagg;
+RESET enable_sort;
+
 -- btree
 drop index hidx;
 create index hidx on testhstore using btree (h);
@@ -331,6 +341,18 @@ set enable_seqscan=off;
 select count(*) from testhstore where h #># 'p=>1';
 select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
 
+--gin hash
+drop index hidx;
+create index hidx on testhstore using gin (h gin_hstore_hash_ops);
+set enable_seqscan=off;
+
+select count(*) from testhstore where h @> 'wait=>NULL';
+select count(*) from testhstore where h @> 'wait=>CC';
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+
+RESET enable_seqscan;
+drop index hidx;
+
 -- json
 select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
diff --git a/contrib/hstore/sql/nested.sql b/contrib/hstore/sql/nested.sql
new file mode 100644
index 0000000..71a692e
--- /dev/null
+++ b/contrib/hstore/sql/nested.sql
@@ -0,0 +1,478 @@
+
+SELECT 'ff => {a=>12, b=>16}'::hstore;
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123'::hstore;
+
+SELECT 'aa => {a,aaa}, qq=>{ a=>12, b=>16 , c=> { c1, c2}, d=>{d1=>d1, d2=>d2, d1=>d3} }'::hstore;
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+
+SELECT 'ff => {a,aaa}'::hstore;
+
+
+select 'null'::hstore;
+select '{null}'::hstore;
+select ''::hstore;
+select '{}'::hstore;
+
+--test optional outer braces
+SELECT	'a=>1'::hstore;
+SELECT	'{a=>1}'::hstore;
+SELECT	'{a,b}'::hstore;
+SELECT	'{a,{b}}'::hstore;
+SELECT	'{{a},b}'::hstore;
+SELECT	'{a,{b},{c}}'::hstore;
+SELECT	'{{a},{b},c}'::hstore;
+SELECT	'{{a},b,{c}}'::hstore;
+SELECT	'{a,{b=>1}}'::hstore;
+SELECT	'{{a},{b=>1}}'::hstore;
+SELECT	'a'::hstore;
+SELECT	'{a}'::hstore;
+SELECT	''::hstore;
+SELECT	'{}'::hstore;
+
+--nested json
+
+SELECT	hstore_to_json('a=>1');
+SELECT	hstore_to_json('{a=>1}');
+SELECT	hstore_to_json('{a,b}');
+SELECT	hstore_to_json('{a,{b}}');
+SELECT	hstore_to_json('{{a},b}');
+SELECT	hstore_to_json('{a,{b},{c}}');
+SELECT	hstore_to_json('{{a},{b},c}');
+SELECT	hstore_to_json('{{a},b,{c}}');
+SELECT	hstore_to_json('{a,{b=>1}}');
+SELECT	hstore_to_json('{{a},{b=>1}}');
+SELECT	hstore_to_json('{{a},{b=>1},{c}}');
+SELECT	hstore_to_json('a');
+SELECT	hstore_to_json('{a}');
+SELECT	hstore_to_json('');
+SELECT	hstore_to_json('{}');
+
+SELECT hstore_to_json('"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore);
+
+--
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'ff', 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'qq', 
+	   ('ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'Y') IS NULL AS t, 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'x'; 
+
+SELECT '[ a, b, c, d]'::hstore -> 'a';
+--
+
+CREATE TABLE testtype (i int, h hstore, a int[], j json);
+INSERT INTO testtype VALUES (1, 'a=>1', '{1,2,3}', '{"x": 2}');
+
+SELECT populate_record(v, 'i=>2'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, a=>{7,8,9}'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}'::hstore) FROM testtype v;
+
+--complex delete
+
+SELECT 'b=>{a,c}'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>1'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+SELECT '[2,3,a]'::hstore - 'a'::text;
+SELECT '[a,2,3,a]'::hstore - 'a'::text;
+SELECT '[a,a]'::hstore - 'a'::text;
+SELECT '[a]'::hstore - 'a'::text;
+SELECT 'a=>1'::hstore - 'a'::text;
+SELECT ''::hstore - 'a'::text;
+
+SELECT '{a, 1 , b,2, c,3}'::hstore - ARRAY['d','b'];
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>23'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>{1,2}'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'a=>{1,2}'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v=>23'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,23]'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,{1,2}]'::hstore;
+
+--joining
+
+SELECT 'aa=>1 , b=>2, cq=>3'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+SELECT '{aa,1 , b,2, cq,3}'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+
+--slice
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['b','c']);
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+SELECT slice_array(hstore 'aa=>1, b=>{2=>1}, c=>{1,2}', ARRAY['b','c']);
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['g','h','i']);
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['b','c']);
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+SELECT slice(hstore '{aa=>1, b=>{2=>1}, c=>{1,2}}', ARRAY['b','c']);
+
+--to array
+SELECT %% 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL';
+SELECT %% '{aa,1, cq,l, b,g, fg,NULL}';
+SELECT hstore_to_matrix( 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL');
+SELECT hstore_to_matrix( '{aa,1, cq,l, b,g, fg,NULL}');
+
+
+--contains
+SELECT 'a=>b'::hstore @> 'a=>b, c=>b';
+SELECT 'a=>b, c=>b'::hstore @> 'a=>b';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{2,1}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1=>2}';
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1=>2}';
+SELECT '{a,b}'::hstore @> '{a,b, c,b}';
+SELECT '{a,b, c,b}'::hstore @> '{a,b}';
+SELECT '{a,b, c,{1,2}}'::hstore @> '{a,{1,2}}';
+SELECT '{a,b, c,{1,2}}'::hstore @> '{b,{1,2}}';
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1}';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{2}';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{3}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{c=>3}}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4}}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},3}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},1}';
+
+-- %>
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'n';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'a';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'b';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'c';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd' %> '1';
+
+SELECT '[1,2,3,{a,b}]'::hstore %> '1';
+SELECT '["1",2,3,{a,b}]'::hstore %> '1';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 0;
+
+-- ->
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 0;
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -6;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -1;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -6;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -1;
+
+-- #>
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{a}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -4}';
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{0}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{3}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4,5}';
+
+-- #%>
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{a}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -4}';
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{0}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{3}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4,5}';
+
+-- ?
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 0;
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -6;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -1;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -6;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -1;
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{0}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{a}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{b}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 0}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 1}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 2}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 3}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -1}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -2}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -3}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -4}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -5}'::text[];
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{0}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{3}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4,5}'::text[];
+
+--deep delete
+
+SELECT 'a=>1'::hstore #- '{x}';
+SELECT 'a=>1'::hstore #- '{a}';
+SELECT 'a=>1'::hstore #- '{NULL}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c}';
+SELECT 'a=>1'::hstore #- '{x,1}';
+SELECT 'a=>1'::hstore #- '{a,1}';
+SELECT 'a=>1'::hstore #- '{NULL,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c,1}';
+
+SELECT '[a]'::hstore #- '{2}';
+SELECT '[a]'::hstore #- '{1}';
+SELECT '[a]'::hstore #- '{0}';
+SELECT '[a]'::hstore #- '{-1}';
+SELECT '[a]'::hstore #- '{-2}';
+
+SELECT '[a,b,c]'::hstore #- '{3}';
+SELECT '[a,b,c]'::hstore #- '{2}';
+SELECT '[a,b,c]'::hstore #- '{1}';
+SELECT '[a,b,c]'::hstore #- '{0}';
+SELECT '[a,b,c]'::hstore #- '{-1}';
+SELECT '[a,b,c]'::hstore #- '{-2}';
+SELECT '[a,b,c]'::hstore #- '{-3}';
+SELECT '[a,b,c]'::hstore #- '{-4}';
+
+SELECT '[a,b,c]'::hstore #- '{0,0}';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{x}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{a}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d}';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}' #- '{b, -1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 2}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, -2}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}' #- '{d, 1, 0}';
+
+-- delete(int)
+
+SELECT '[a,b,c]'::hstore - 3;
+SELECT '[a,b,c]'::hstore - 2;
+SELECT '[a,b,c]'::hstore - 1;
+SELECT '[a,b,c]'::hstore - 0;
+SELECT '[a,b,c]'::hstore - -1;
+SELECT '[a,b,c]'::hstore - -2;
+SELECT '[a,b,c]'::hstore - -3;
+SELECT '[a,b,c]'::hstore - -4;
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 3;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 2;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 1;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 0;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -1;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -2;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -3;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -4;
+
+--replace
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b,-1}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1,0}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,NULL,0}', '{1,2,3}');
+
+--deep concat
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'n=>not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'n=>not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{not_null}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'b=>{3,4}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b}', '{3,4}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '{4,5}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '4=>5');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d}', '2=>{4,5}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{NULL,1}', '4=>5');
+
+--cast 
+
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::text)::hstore AS err;
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json)::hstore AS ok;
+
+--hvals
+
+SELECT q->'tags' FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore) AS q;
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+
+--svals path
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+
+--each
+
+SELECT * FROM each('a=>b, c=>cc'::hstore) AS q;
+SELECT * FROM each('[a, b, c, cc]'::hstore) AS q;
+SELECT * FROM each('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+
+SELECT * FROM each_hstore('a=>b, c=>cc'::hstore) AS q;
+SELECT * FROM each_hstore('[a, b, c, cc]'::hstore) AS q;
+SELECT * FROM each_hstore('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+
+--decoration
+
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]]'::hstore AS h, '[a, {b=>c}, [c, d, e]]'::hstore AS a;
+
+SET hstore.pretty_print = true;
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]], e=>[1,2,3,4], f=>g, g=>j'::hstore AS h, 
+	   '[a, {b=>c, c=>d}, [c, d, e, [1,2], h, {f=>g, g=>f}]]'::hstore AS a;
+RESET hstore.pretty_print;
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore);
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, array_curly_braces := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, loose := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, array_curly_braces := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, array_curly_braces := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true, loose := true);
diff --git a/contrib/hstore/sql/types.sql b/contrib/hstore/sql/types.sql
new file mode 100644
index 0000000..ac0b206
--- /dev/null
+++ b/contrib/hstore/sql/types.sql
@@ -0,0 +1,147 @@
+SELECT '"foo"=>true'::hstore;
+SELECT 'foo=>true'::hstore;
+SELECT '"true"=>true'::hstore;
+SELECT 'true=>true'::hstore;
+SELECT '"t"=>true'::hstore;
+SELECT 't=>true'::hstore;
+SELECT '"false"=>true'::hstore;
+SELECT 'false=>true'::hstore;
+SELECT '"f"=>true'::hstore;
+SELECT 'f=>true'::hstore;
+
+SELECT '"foo"=>false'::hstore;
+SELECT 'foo=>false'::hstore;
+SELECT '"false"=>false'::hstore;
+SELECT 'false=>false'::hstore;
+SELECT '"t"=>false'::hstore;
+SELECT 't=>false'::hstore;
+SELECT '"false"=>false'::hstore;
+SELECT 'false=>false'::hstore;
+SELECT '"f"=>false'::hstore;
+SELECT 'f=>false'::hstore;
+
+SELECT '"1"=>x'::hstore;
+SELECT '1=>x'::hstore;
+SELECT 'foo=>1'::hstore;
+SELECT 'foo=>1.'::hstore;
+SELECT 'foo=>1.0'::hstore;
+SELECT 'foo=>1.01'::hstore;
+SELECT 'foo=>1.01e'::hstore;
+SELECT 'foo=>1.01e1'::hstore;
+SELECT 'foo=>1.01e+1'::hstore;
+SELECT 'foo=>1.01e-1'::hstore;
+SELECT 'foo=>.1'::hstore;
+SELECT 'foo=>.1e'::hstore;
+SELECT 'foo=>.1e1'::hstore;
+SELECT 'foo=>.1e+1'::hstore;
+SELECT 'foo=>.1e-1'::hstore;
+
+SELECT 'foo=>+1'::hstore;
+SELECT 'foo=>+1.'::hstore;
+SELECT 'foo=>+1.0'::hstore;
+SELECT 'foo=>+1.01'::hstore;
+SELECT 'foo=>+1.01e'::hstore;
+SELECT 'foo=>+1.01e1'::hstore;
+SELECT 'foo=>+1.01e+1'::hstore;
+SELECT 'foo=>+1.01e-1'::hstore;
+SELECT 'foo=>+.1'::hstore;
+SELECT 'foo=>+.1e'::hstore;
+SELECT 'foo=>+.1e1'::hstore;
+SELECT 'foo=>+.1e+1'::hstore;
+SELECT 'foo=>+.1e-1'::hstore;
+
+SELECT 'foo=>-1'::hstore;
+SELECT 'foo=>-1.'::hstore;
+SELECT 'foo=>-1.0'::hstore;
+SELECT 'foo=>-1.01'::hstore;
+SELECT 'foo=>-1.01e'::hstore;
+SELECT 'foo=>-1.01e1'::hstore;
+SELECT 'foo=>-1.01e+1'::hstore;
+SELECT 'foo=>-1.01e-1'::hstore;
+SELECT 'foo=>-.1'::hstore;
+SELECT 'foo=>-.1e'::hstore;
+SELECT 'foo=>-.1e1'::hstore;
+SELECT 'foo=>-.1e+1'::hstore;
+SELECT 'foo=>-.1e-1'::hstore;
+
+SELECT 'foo=>1e2000'::hstore;
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'foo';
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'bar';
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 0;
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 1;
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'foo';
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'bar';
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 0;
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 1;
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 0}';
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 1}';
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'foo';
+SELECT 'foo=>t, bar=>x'::hstore ?> 'bar';
+SELECT 'foo=>t, bar=>x'::hstore ?> 0;
+SELECT 'foo=>t, bar=>x'::hstore ?> 1;
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'foo';
+SELECT '[foo, t, bar, x]'::hstore ?> 'bar';
+SELECT '[foo, t, bar, x]'::hstore ?> 0;
+SELECT '[foo, t, bar, x]'::hstore ?> 1;
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 0}';
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 1}';
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'foo';
+SELECT 'foo=>f, bar=>x'::hstore ?> 'bar';
+SELECT 'foo=>f, bar=>x'::hstore ?> 0;
+SELECT 'foo=>f, bar=>x'::hstore ?> 1;
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'foo';
+SELECT '[foo, f, bar, x]'::hstore ?> 'bar';
+SELECT '[foo, f, bar, x]'::hstore ?> 0;
+SELECT '[foo, f, bar, x]'::hstore ?> 1;
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 0}';
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 1}';
+
+
+SELECT hstore_typeof('a=>b') AS hash;
+SELECT hstore_typeof('{a=>b}') AS hash;
+SELECT hstore_typeof('{a, b}') AS array;
+SELECT hstore_typeof('{{a=>b}}') AS array;
+SELECT hstore_typeof('[a, b]') AS array;
+SELECT hstore_typeof('') AS "NULL";
+SELECT hstore_typeof('NULL') AS "null";
+SELECT hstore_typeof('1.0') AS numeric;
+SELECT hstore_typeof('t') AS bool;
+SELECT hstore_typeof('f') AS bool;
+
+SELECT hstore('xxx', 't'::bool);
+SELECT hstore('xxx', 'f'::bool);
+
+SELECT hstore('xxx', 3.14);
+SELECT hstore('xxx', 3.14::numeric);
+SELECT hstore('xxx', '3.14'::numeric);
+
+SELECT hstore(NULL);
+SELECT hstore('NULL');
+
+SELECT hstore('t'::bool) AS "true", hstore('f'::bool) AS "false";
+
+SELECT hstore(3.14), hstore(3.14::numeric), hstore('3.14'::numeric);
+
+SELECT hstore('xxx', 'foo=>t, bar=>3.14, zzz=>xxx'::hstore);
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int2[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int4[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int8[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float4[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float8[]);
+SELECT array_to_hstore('{{1,1,f},{f,t,NULL}}'::bool[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::text[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::varchar[]);
+
+SELECT array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]);
+SELECT hstore('array', array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]));
+
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index e5b9e66..4b854f9 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -12,7 +12,7 @@
    <productname>PostgreSQL</> also provides event triggers.  Unlike regular
    triggers, which are attached to a single table and capture only DML events,
    event triggers are global to a particular database and are capable of
-   capturing DDL events.
+   capturing DDL events or transaction commits.
   </para>
 
   <para>
@@ -29,8 +29,9 @@
      occurs in the database in which it is defined. Currently, the only
      supported events are
      <literal>ddl_command_start</>,
-     <literal>ddl_command_end</>
-     and <literal>sql_drop</>.
+     <literal>ddl_command_end</>,
+     <literal>sql_drop</>, and
+     <literal>transaction_commit</>.
      Support for additional events may be added in future releases.
    </para>
 
@@ -65,6 +66,15 @@
    </para>
 
    <para>
+    A <literal>transaction_commit</> trigger is called at the end of a
+    transaction, just before any deferred triggers are fired, unless
+    no data changes have been made by the transaction, or
+    <productname>PostgreSQL</> is running in Single-User mode. This is so
+    that you can recover from a badly specified <literal>transaction_commit</>
+    trigger.
+   </para>
+
+   <para>
      Event triggers (like other functions) cannot be executed in an aborted
      transaction.  Thus, if a DDL command fails with an error, any associated
      <literal>ddl_command_end</> triggers will not be executed.  Conversely,
@@ -77,8 +87,13 @@
    </para>
 
    <para>
-     For a complete list of commands supported by the event trigger mechanism,
-     see <xref linkend="event-trigger-matrix">.
+    A <literal>transaction_commit</> trigger is also not called in an
+    aborted transaction.
+   </para>
+
+   <para>
+     For a complete list of commands supported by the event trigger
+     mechanism, see <xref linkend="event-trigger-matrix">.
    </para>
 
    <para>
@@ -101,6 +116,11 @@
      to intercept. A common use of such triggers is to restrict the range of
      DDL operations which users may perform.
    </para>
+
+   <para>
+    <literal>transaction_commit</> triggers do not currently support
+    <literal>WHEN</literal> clauses.
+   </para>
   </sect1>
 
   <sect1 id="event-trigger-matrix">
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index fbe9543..9f6a5bc 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -8,39 +8,108 @@
  </indexterm>
 
  <para>
-  This module implements the <type>hstore</> data type for storing sets of
-  key/value pairs within a single <productname>PostgreSQL</> value.
+  This module implements the <type>hstore</> data type for storing arbitrarily
+  nested key/value pairs and arrays 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 and values are
-  simply text strings.
+  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.
  </para>
 
  <sect2>
   <title><type>hstore</> External Representation</title>
 
   <para>
-
    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:
+   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 continaining 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 represnted as <literal>true</>, <literal>t</>,
+   <literal>false</>, or <literal>f</>. Use quotation marks to represent thes
+   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 includes zero or more
+   <replaceable>key</> <literal>=&gt;</> <replaceable>value</> pairs separated
+   by commas, optionally brackted by curly braces. Keys must be strings and
+   may not be <literal>NULL</>; values may be any <type>hstore</> type,
+   including <literal>NULL</>. Examples:
 
-<synopsis>
-k =&gt; v
-foo =&gt; bar, baz =&gt; whatever
-"1-a" =&gt; "anything at all"
-</synopsis>
+<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>
 
    The order of the pairs is not significant (and may not be reproduced on
-   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.
+   output).
   </para>
 
   <para>
-   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:
+   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:
 
 <programlisting>
 SELECT 'a=&gt;1,a=&gt;2'::hstore;
@@ -51,14 +120,58 @@ SELECT 'a=&gt;1,a=&gt;2'::hstore;
   </para>
 
   <para>
-   A value (but not a key) can be an SQL <literal>NULL</>. For example:
+   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>:
 
 <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>.
+=% 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>
   </para>
 
   <note>
@@ -83,6 +196,36 @@ key =&gt; NULL
  </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>
 
   <para>
@@ -98,6 +241,7 @@ key =&gt; NULL
     <thead>
      <row>
       <entry>Operator</entry>
+      <entry>Returns</entry>
       <entry>Description</entry>
       <entry>Example</entry>
       <entry>Result</entry>
@@ -107,13 +251,103 @@ key =&gt; NULL
     <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>t</literal></entry>
+     </row>
+
+     <row>
+      <entry><type>hstore</> <literal>?&gt;</> <type>integer</></entry>
+      <entry><type>bolean</></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>f</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 booelan or not present)</entry>
+      <entry><literal>'foo =&gt; {bar =&gt; true}'::hstore #?&gt; '[foo,bar]'</literal></entry>
+      <entry><literal>t</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>
@@ -121,6 +355,7 @@ key =&gt; NULL
 
      <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>
@@ -128,13 +363,31 @@ key =&gt; NULL
 
      <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>t</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>t</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>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>t</literal></entry>
@@ -142,6 +395,7 @@ key =&gt; NULL
 
      <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>t</literal></entry>
@@ -149,6 +403,7 @@ key =&gt; NULL
 
      <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>t</literal></entry>
@@ -156,6 +411,7 @@ key =&gt; NULL
 
      <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>f</literal></entry>
@@ -163,13 +419,23 @@ key =&gt; NULL
 
      <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>
+     </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>
@@ -177,13 +443,23 @@ key =&gt; NULL
 
      <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>
+     </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>
@@ -191,6 +467,7 @@ key =&gt; NULL
 
      <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>
@@ -198,6 +475,7 @@ key =&gt; NULL
 
      <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>
@@ -208,12 +486,22 @@ key =&gt; NULL
   </table>
 
   <note>
-  <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>
+    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>
   </note>
 
@@ -258,6 +546,14 @@ key =&gt; NULL
      </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>
       <entry>make single-item <type>hstore</></entry>
@@ -266,6 +562,54 @@ key =&gt; NULL
      </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>
       <entry>get <type>hstore</>'s keys as an array</entry>
@@ -306,6 +650,18 @@ b
      </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>
       <entry>get <type>hstore</>'s keys and values as an array of alternating
@@ -339,6 +695,14 @@ b
      </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>
+     </row>
+
+     <row>
       <entry><function>slice(hstore, text[])</function><indexterm><primary>slice</primary></indexterm></entry>
       <entry><type>hstore</type></entry>
       <entry>extract a subset of an <type>hstore</></entry>
@@ -361,6 +725,20 @@ b
      </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>
       <entry>does <type>hstore</> contain key?</entry>
@@ -377,6 +755,30 @@ b
      </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;}</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>
@@ -404,7 +806,15 @@ b
       <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 Examples section</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></entry>
      </row>
 
@@ -415,7 +825,8 @@ b
   <note>
    <para>
      The function <function>hstore_to_json</function> is used when an <type>hstore</type>
-     value is cast to <type>json</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>.
    </para>
   </note>
 
@@ -426,6 +837,14 @@ 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>
@@ -458,6 +877,155 @@ CREATE INDEX hidx ON testhstore USING HASH (h);
  </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 add 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>Retuns 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</a> 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>
 
   <para>
@@ -489,20 +1057,6 @@ SELECT hstore(t) FROM test AS t;
   </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>
-
-  <para>
    Modify an existing record using the values from an <type>hstore</>:
 <programlisting>
 CREATE TABLE test (col1 integer, col2 text, col3 text);
@@ -567,11 +1121,14 @@ SELECT key, count(*) FROM
  <sect2>
   <title>Compatibility</title>
 
-  <para>
-   As of PostgreSQL 9.0, <type>hstore</> uses a different internal
-   representation than previous versions. This presents no obstacle for
+  <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
    dump/restore upgrades since the text representation (used in the dump) is
-   unchanged.
+   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.
   </para>
 
   <para>
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 356890d..f41d6fc 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -203,6 +203,7 @@ distprep:
 	$(MAKE) -C catalog	schemapg.h postgres.bki postgres.description postgres.shdescription
 	$(MAKE) -C replication	repl_gram.c repl_scanner.c
 	$(MAKE) -C utils	fmgrtab.c fmgroids.h errcodes.h
+	$(MAKE) -C utils/adt	jsonb_gram.c jsonb_scan.c
 	$(MAKE) -C utils/misc	guc-file.c
 	$(MAKE) -C utils/sort	qsort_tuple.c
 
@@ -320,6 +321,9 @@ maintainer-clean: distclean
 	      utils/fmgroids.h \
 	      utils/fmgrtab.c \
 	      utils/errcodes.h \
+	      utils/adt/jsonb_gram.c \
+	      utils/adt/jsonb_gram.h \
+	      utils/adt/jsonb_scan.c \
 	      utils/misc/guc-file.c \
 	      utils/sort/qsort_tuple.c
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 0487be1..912cf6a 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -30,6 +30,7 @@
 #include "catalog/namespace.h"
 #include "catalog/storage.h"
 #include "commands/async.h"
+#include "commands/event_trigger.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
 #include "executor/spi.h"
@@ -1866,6 +1867,16 @@ CommitTransaction(void)
 	Assert(s->parent == NULL);
 
 	/*
+	 * First fire any pre-commit triggers, so if they in turn cause any
+	 * deferred triggers etc to fire this will be picked up below.
+	 * Only fire them, though, if we have a real transaction ID and
+	 * we're not running standalone. Not firing when standalone provides
+	 * a way to recover from setting up a bad transaction trigger.
+	 */
+	if (s->transactionId != InvalidTransactionId && IsUnderPostmaster)
+		PreCommitTriggersFire();
+
+	/*
 	 * Do pre-commit processing that involves calling user-defined code, such
 	 * as triggers.  Since closing cursors could queue trigger actions,
 	 * triggers could open cursors, etc, we have to keep looping until there's
@@ -2071,6 +2082,16 @@ PrepareTransaction(void)
 	Assert(s->parent == NULL);
 
 	/*
+	 * First fire any pre-commit triggers, so if they in turn cause any
+	 * deferred triggers etc to fire this will be picked up below.
+	 * Only fire them, though, if we have a real transaction ID and
+	 * we're not running standalone. Not firing when standalone provides
+	 * a way to recover from setting up a bad transaction trigger.
+	 */
+	if (s->transactionId != InvalidTransactionId && IsUnderPostmaster)
+		PreCommitTriggersFire();
+
+	/*
 	 * Do pre-commit processing that involves calling user-defined code, such
 	 * as triggers.  Since closing cursors could queue trigger actions,
 	 * triggers could open cursors, etc, we have to keep looping until there's
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 043d118..ca6d14c 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -796,3 +796,11 @@ CREATE OR REPLACE FUNCTION
 CREATE OR REPLACE FUNCTION
   json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
   RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'json_populate_recordset';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_record(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS anyelement LANGUAGE internal STABLE AS 'jsonb_populate_record';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_recordset(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'jsonb_populate_recordset';
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 59f0842..2d6eb56 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -153,7 +153,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 	/* Validate event name. */
 	if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
 		strcmp(stmt->eventname, "ddl_command_end") != 0 &&
-		strcmp(stmt->eventname, "sql_drop") != 0)
+		strcmp(stmt->eventname, "sql_drop") != 0 &&
+		strcmp(stmt->eventname, "transaction_commit") != 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("unrecognized event name \"%s\"",
@@ -1294,3 +1295,42 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
 
 	return (Datum) 0;
 }
+
+/*
+ * PreCommitTriggersFire
+ *
+ * fire triggers set for the commit event.
+ *
+ * This will be called just before deferred RI and Constraint triggers are
+ * fired.
+ */
+void
+PreCommitTriggersFire(void)
+{
+	List * trigger_list;
+	EventTriggerData trigdata;
+	List	   *runlist = NIL;
+	ListCell   *lc;
+
+	trigger_list = EventCacheLookup(EVT_Commit);
+
+	foreach(lc, trigger_list)
+	{
+		EventTriggerCacheItem *item = lfirst(lc);
+
+		runlist = lappend_oid(runlist, item->fnoid);
+	}
+
+	/* don't spend any more time on this if no functions to run */
+	if (runlist == NIL)
+		return;
+
+	trigdata.type = T_EventTriggerData;
+	trigdata.event = "transaction_commit";
+	trigdata.parsetree = NULL;
+	trigdata.tag = "COMMIT";
+
+	EventTriggerInvoke(runlist, &trigdata);
+
+	list_free(runlist);
+}
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
 	tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
 	tsvector.o tsvector_op.o tsvector_parser.o \
 	txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-	rangetypes_typanalyze.o rangetypes_selfuncs.o
+	rangetypes_typanalyze.o rangetypes_selfuncs.o \
+	jsonb.o jsonb_support.o
 
 like.o: like.c like_match.c
 
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 21a2336..46f4504 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -1804,12 +1804,15 @@ escape_json(StringInfo buf, const char *str)
 Datum
 json_typeof(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonTokenType tok;
 	char *type;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
+
 	/* Lex exactly one token from the input and check its type. */
 	json_lex(lex);
 	tok = lex_peek(lex);
@@ -1840,3 +1843,4 @@ json_typeof(PG_FUNCTION_ARGS)
 
 	PG_RETURN_TEXT_P(cstring_to_text(type));
 }
+
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
new file mode 100644
index 0000000..2b34a93
--- /dev/null
+++ b/src/backend/utils/adt/jsonb.c
@@ -0,0 +1,565 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.c
+ *		I/O for jsonb type 
+ *
+ * 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
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/json.h"
+#include "utils/jsonapi.h"
+#include "utils/jsonb.h"
+
+static size_t
+checkStringLen(size_t len)
+{
+	 if (len > JSONB_MAX_STRING_LEN)
+		  ereport(ERROR,
+					 (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+					  errmsg("string too long for jsonb string")));
+	 return len;
+}
+
+static Jsonb*
+dumpJsonb(JsonbValue *p)
+{
+	 uint32			 buflen;
+	 Jsonb			*out;
+
+	 if (p == NULL)
+	 {
+		  buflen = 0;
+		  out = palloc(VARHDRSZ);
+	 }
+	 else
+	 {
+		  buflen = VARHDRSZ + p->size;
+		  out = palloc(buflen);
+		  SET_VARSIZE(out, buflen);
+
+		  buflen = compressJsonb(p, VARDATA(out));
+	 }
+	 SET_VARSIZE(out, buflen + VARHDRSZ);
+
+	 return out;
+}
+
+typedef struct JsonbInState
+{
+	ToJsonbState	*state;
+	JsonbValue		*res;
+}	JsonbInState;
+
+
+/*
+ * for jsonb we always want the de-escaped value - that's what's in token 
+ */
+
+static void 
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+	JsonbValue		v;
+
+	v.size = sizeof(JEntry);
+
+	switch (tokentype)
+	{
+			
+	case JSON_TOKEN_STRING:
+		v.type = jbvString;
+		v.string.len = token ? checkStringLen(strlen(token)) : 0;
+		v.string.val = token ? pnstrdup(token, v.string.len) : NULL;
+		v.size += v.string.len;
+		break;
+	case JSON_TOKEN_NUMBER:
+		v.type = jbvNumeric;
+		v.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
+		v.size += VARSIZE_ANY(v.numeric) + sizeof(JEntry) /* alignment */;
+		break;
+	case JSON_TOKEN_TRUE:
+		v.type = jbvBool;
+		v.boolean = true;
+		break;
+	case JSON_TOKEN_FALSE:
+		v.type = jbvBool;
+		v.boolean = false;
+		break;
+	case JSON_TOKEN_NULL:
+		v.type = jbvNull;
+		break;
+	default: /* nothing else should be here in fact */
+		break;
+	}
+
+	if (_state->state == NULL)
+	{
+		/* single scalar */
+		JsonbValue	va;
+
+		va.type = jbvArray;
+		va.array.scalar = true;
+		va.array.nelems = 1;
+
+		_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, &va);
+		_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+		_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+	}
+	else
+	{
+		JsonbValue	*o = &_state->state->v;
+
+		switch(o->type)
+		{
+			case jbvArray:
+				_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+				break;
+			case jbvHash:
+				_state->res = pushJsonbValue(&_state->state, WJB_VALUE, &v);
+				break;
+			default:
+				elog(ERROR, "Wrong state");
+		}
+	}
+}
+
+static void
+jsonb_in_object_start(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_OBJECT, NULL);
+}
+
+static void
+jsonb_in_object_end(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_OBJECT, NULL);
+}
+
+static void
+jsonb_in_array_start(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, NULL);
+}
+
+static void
+jsonb_in_array_end(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+}
+
+static void
+jsonb_in_object_field_start(void *state, char *fname, bool isnull)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+	JsonbValue		v;
+
+	v.type = jbvString;
+	v.string.len = fname ? checkStringLen(strlen(fname)) : 0;
+	v.string.val = fname ? pnstrdup(fname, v.string.len) : NULL;
+	v.size = sizeof(JEntry) + v.string.len;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_KEY, &v);
+}
+
+Datum
+jsonb_in(PG_FUNCTION_ARGS)
+{
+	char	   		*json = PG_GETARG_CSTRING(0);
+	text	   		*result = cstring_to_text(json);
+	JsonLexContext 	*lex;
+	JsonbInState 	state;
+	JsonSemAction 	sem;
+
+	memset(&state, 0, sizeof(state));
+	memset(&sem, 0, sizeof(sem));
+	lex = makeJsonLexContext(result, true);
+
+	sem.semstate = (void *) &state;
+
+	sem.object_start = jsonb_in_object_start;
+	sem.array_start = jsonb_in_array_start;
+	sem.object_end = jsonb_in_object_end;
+	sem.array_end = jsonb_in_array_end;
+	sem.scalar = jsonb_in_scalar;
+	sem.object_field_start = jsonb_in_object_field_start;
+
+	pg_parse_json(lex, &sem);
+
+	/* after parsing, the item membar has the composed jsonn structure */
+	PG_RETURN_POINTER(dumpJsonb(state.res));
+}
+
+static void recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header);
+
+static void
+recvJsonbValue(StringInfo buf, JsonbValue *v, uint32 level, int c)
+{
+	 uint32  hentry = c & JENTRY_TYPEMASK;
+
+	 if (hentry == JENTRY_ISNULL)
+	 {
+		  v->type = jbvNull;
+		  v->size = sizeof(JEntry);
+	 }
+	 else if (hentry == JENTRY_ISOBJECT || hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	 {
+		  recvJsonb(buf, v, level + 1, (uint32)c);
+	 }
+	 else if (hentry == JENTRY_ISFALSE || hentry == JENTRY_ISTRUE)
+	 {
+		  v->type = jbvBool;
+		  v->size = sizeof(JEntry);
+		  v->boolean = (hentry == JENTRY_ISFALSE) ? false : true;
+	 }
+	 else if (hentry == JENTRY_ISNUMERIC)
+	 {
+		  v->type = jbvNumeric;
+		  v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv, PointerGetDatum(buf),
+																			Int32GetDatum(0), Int32GetDatum(-1)));
+		  v->size = sizeof(JEntry) * 2 + VARSIZE_ANY(v->numeric);
+	 }
+	 else if (hentry == JENTRY_ISSTRING)
+	 {
+		  v->type = jbvString;
+		  v->string.val = pq_getmsgtext(buf, c, &c);
+		  v->string.len = checkStringLen(c);
+		  v->size = sizeof(JEntry) + v->string.len;
+	 }
+	 else
+	 {
+		  elog(ERROR, "bogus input");
+	 }
+}
+
+static void
+recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header)
+{
+	 uint32  hentry;
+	 uint32  i;
+
+	 hentry = header & JENTRY_TYPEMASK;
+
+	 v->size = 3 * sizeof(JEntry);
+	 if (hentry == JENTRY_ISOBJECT)
+	 {
+		  v->type = jbvHash;
+		  v->hash.npairs = header & JB_COUNT_MASK;
+		  if (v->hash.npairs > 0)
+		  {
+				v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+				for(i=0; i<v->hash.npairs; i++)
+				{
+					 recvJsonbValue(buf, &v->hash.pairs[i].key, level, pq_getmsgint(buf, 4));
+					 if (v->hash.pairs[i].key.type != jbvString)
+						  elog(ERROR, "jsonb's key could be only a string");
+
+					 recvJsonbValue(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;
+				}
+
+				uniqueJsonbValue(v);
+		  }
+	 }
+	 else if (hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	 {
+		  v->type = jbvArray;
+		  v->array.nelems = header & JB_COUNT_MASK;
+		  v->array.scalar = (hentry == JENTRY_ISCALAR) ? true : false;
+
+		  if (v->array.scalar && v->array.nelems != 1)
+				elog(ERROR, "bogus input");
+
+		  if (v->array.nelems > 0)
+		  {
+				v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+				for(i=0; i<v->array.nelems; i++)
+				{
+					 recvJsonbValue(buf, v->array.elems + i, level, pq_getmsgint(buf, 4));
+					 v->size += v->array.elems[i].size;
+				}
+		  }
+	 }
+	 else
+	 {
+				elog(ERROR, "bogus input");
+	 }
+}
+
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	 StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
+	 JsonbValue v;
+
+	 recvJsonb(buf, &v, 0, pq_getmsgint(buf, 4));
+
+	 PG_RETURN_POINTER(dumpJsonb(&v));
+}
+
+static void
+putEscapedValue(StringInfo out, JsonbValue *v)
+{
+	 switch(v->type)
+	 {
+		  case jbvNull:
+				appendBinaryStringInfo(out, "null", 4);
+				break;
+		  case jbvString:
+				escape_json(out, pnstrdup(v->string.val, v->string.len));
+				break;
+		  case jbvBool:
+				if (v->boolean)
+					 appendBinaryStringInfo(out, "true", 4);
+				else
+					 appendBinaryStringInfo(out, "false", 5);
+				break;
+		  case jbvNumeric:
+				appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+				break;
+		  default:
+				elog(PANIC, "Unknown type");
+	 }
+}
+
+char*
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{
+	 bool			first = true;
+	 JsonbIterator  *it;
+	 int			type;
+	 JsonbValue	  	v;
+	 int			 level = 0;
+
+	 if (out == NULL)
+		  out = makeStringInfo();
+
+	 if (in == NULL)
+	 {
+		  appendStringInfoString(out, "");
+		  return out->data;
+	 }
+
+	 enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);
+
+	 it = JsonbIteratorInit(in);
+
+	 while((type = JsonbIteratorGet(&it, &v, false)) != 0)
+	 {
+reout:
+		  switch(type)
+		  {
+				case WJB_BEGIN_ARRAY:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+
+					 if (v.array.scalar == false)
+						 appendStringInfoChar(out, '[');
+					 level++;
+					 break;
+				case WJB_BEGIN_OBJECT:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+					 appendStringInfoCharMacro(out, '{');
+
+					 level++;
+					 break;
+				case WJB_KEY:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+
+					 putEscapedValue(out, &v);
+					 appendBinaryStringInfo(out, ": ", 2);
+
+					 type = JsonbIteratorGet(&it, &v, false);
+					 if (type == WJB_VALUE)
+					 {
+						  first = false;
+						  putEscapedValue(out, &v);
+					 }
+					 else
+					 {
+						  Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+						  goto reout;
+					 }
+					 break;
+				case WJB_ELEM:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 else
+						  first = false;
+
+					 putEscapedValue(out, &v);
+					 break;
+				case WJB_END_ARRAY:
+					 level--;
+					 if (v.array.scalar == false)
+						  appendStringInfoChar(out, ']');
+					 first = false;
+					 break;
+				case WJB_END_OBJECT:
+					 level--;
+					 appendStringInfoCharMacro(out, '}');
+					 first = false;
+					 break;
+				default:
+					 elog(PANIC, "Wrong flags");
+		  }
+	 }
+
+	 Assert(level == 0);
+
+	 return out->data;
+}
+
+Datum
+jsonb_out(PG_FUNCTION_ARGS)
+{
+	 Jsonb  *jb = PG_GETARG_JSONB(0);
+	 char	 *out;
+
+	 out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	 PG_RETURN_CSTRING(out);
+}
+
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	 Jsonb			 *in = PG_GETARG_JSONB(0);
+	 StringInfoData  buf;
+
+	 pq_begintypsend(&buf);
+
+	 if (JB_ISEMPTY(in))
+	 {
+		  pq_sendint(&buf, 0, 4);
+	 }
+	 else
+	 {
+		  JsonbIterator  *it;
+		  int				 type;
+		  JsonbValue	  v;
+		  uint32			 flag;
+		  bytea			  *nbuf;
+
+		  enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */);
+
+		  it = JsonbIteratorInit(VARDATA_ANY(in));
+
+		  while((type = JsonbIteratorGet(&it, &v, false)) != 0)
+		  {
+				switch(type)
+				{
+					 case WJB_BEGIN_ARRAY:
+						  flag = (v.array.scalar) ? JENTRY_ISCALAR : JENTRY_ISARRAY;
+						  pq_sendint(&buf, v.array.nelems | flag, 4);
+						  break;
+					 case WJB_BEGIN_OBJECT:
+						  pq_sendint(&buf, v.hash.npairs | JENTRY_ISOBJECT, 4);
+						  break;
+					 case WJB_KEY:
+						  pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+						  pq_sendtext(&buf, v.string.val, v.string.len);
+						  break;
+					 case WJB_ELEM:
+					 case WJB_VALUE:
+						  switch(v.type)
+						  {
+								case jbvNull:
+									 pq_sendint(&buf, JENTRY_ISNULL, 4);
+									 break;
+								case jbvString:
+									 pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+									 pq_sendtext(&buf, v.string.val, v.string.len);
+									 break;
+								case jbvBool:
+									 pq_sendint(&buf, (v.boolean) ? JENTRY_ISTRUE : JENTRY_ISFALSE, 4);
+									 break;
+								case jbvNumeric:
+									 nbuf = DatumGetByteaP(DirectFunctionCall1(numeric_send, NumericGetDatum(v.numeric)));
+									 pq_sendint(&buf, VARSIZE_ANY(nbuf) | JENTRY_ISNUMERIC, 4);
+									 pq_sendbytes(&buf, (char*)nbuf, VARSIZE_ANY(nbuf));
+									 break;
+								default:
+									 elog(PANIC, "Wrong type: %u", v.type);
+						  }
+						  break;
+					 case WJB_END_ARRAY:
+					 case WJB_END_OBJECT:
+						  break;
+					 default:
+						  elog(PANIC, "Wrong flags");
+				}
+		  }
+	 }
+
+	 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{
+	 Jsonb			 *in = PG_GETARG_JSONB(0);
+	 JsonbIterator   *it;
+	 JsonbValue	      v;
+	 char            *result;
+
+	 if (JB_ROOT_IS_OBJECT(in))
+		 result = "object";
+	 else if (JB_ROOT_IS_ARRAY(in) && ! JB_ROOT_IS_SCALAR(in))
+		 result = "array";
+	 else
+	 {
+		 Assert(JB_ROOT_IS_SCALAR(in));
+
+		 it = JsonbIteratorInit(VARDATA_ANY(in));
+		 /* 
+		  * a root scalar is stored as an array of one element, 
+		  * so we get the array and then its first (and only) member.
+		  */
+		 (void) JsonbIteratorGet(&it, &v, true);
+		 (void) JsonbIteratorGet(&it, &v, true);
+		 switch(v.type)
+		 {
+		 case jbvNull:
+			 result = "null";
+			 break;
+		 case jbvString:
+			 result = "string";
+			 break;
+		 case jbvBool:
+			 result = "boolean";
+			 break;
+		 case jbvNumeric:
+			 result = "number";
+			 break;
+		 default:
+			 elog(ERROR, "Wrong jsonb scalar type: %u", v.type);
+		 }
+	 }
+	 
+	PG_RETURN_TEXT_P(cstring_to_text(result));		 
+}
diff --git a/src/backend/utils/adt/jsonb_support.c b/src/backend/utils/adt/jsonb_support.c
new file mode 100644
index 0000000..e23f8b9
--- /dev/null
+++ b/src/backend/utils/adt/jsonb_support.c
@@ -0,0 +1,1217 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb_support.c
+ *    Support functions for jsonb
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/adt/jsonb_support.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "utils/builtins.h"
+#include "utils/jsonb.h"
+
+/*
+ * Sort and unique pairs in hash-like JsonbValue
+ */
+void
+uniqueJsonbValue(JsonbValue *v)
+{
+	bool    hasNonUniq = false;
+
+	Assert(v->type == jbvHash);
+
+	if (v->hash.npairs > 1)
+		qsort_arg(v->hash.pairs, v->hash.npairs, sizeof(*v->hash.pairs),
+				  compareJsonbPair, &hasNonUniq);
+
+	if (hasNonUniq)
+	{
+		JsonbPair	*ptr = v->hash.pairs + 1,
+					*res = v->hash.pairs;
+
+		while(ptr - v->hash.pairs < v->hash.npairs)
+		{
+			if (ptr->key.string.len == res->key.string.len &&
+				memcmp(ptr->key.string.val, res->key.string.val,
+				ptr->key.string.len) == 0)
+			{
+				v->size -= ptr->key.size + ptr->value.size;
+			}
+			else
+			{
+				res++;
+				if (ptr != res)
+					memcpy(res, ptr, sizeof(*res));
+			}
+			ptr++;
+		}
+
+		v->hash.npairs = res + 1 - v->hash.pairs;
+	}
+}
+
+/****************************************************************************
+ *                         Compare Functions                                *
+ ****************************************************************************/
+int
+compareJsonbStringValue(const void *a, const void *b, void *arg)
+{
+	const JsonbValue  *va = a;
+	const JsonbValue  *vb = b;
+	int					res;
+
+	Assert(va->type == jbvString);
+	Assert(vb->type == jbvString);
+
+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool*)arg = true;
+	}
+	else
+	{
+		res = (va->string.len > vb->string.len) ? 1 : -1;
+	}
+
+	return res;
+}
+
+int
+compareJsonbPair(const void *a, const void *b, void *arg)
+{
+	const 	JsonbPair *pa = a;
+	const 	JsonbPair *pb = b;
+	int 	res;
+
+	res = compareJsonbStringValue(&pa->key, &pb->key, arg);
+
+	/*
+	 * guarantee keeping order of equal pair. Unique algorithm will
+	 * prefer first element as value
+	 */
+
+	if (res == 0)
+		res = (pa->order > pb->order) ? -1 : 1;
+
+	return res;
+}
+
+int
+compareJsonbValue(JsonbValue *a, JsonbValue *b)
+{
+	if (a->type == b->type)
+	{
+		switch(a->type)
+		{
+			case jbvNull:
+				return 0;
+			case jbvString:
+				return compareJsonbStringValue(a, b, NULL);
+			case jbvBool:
+				if (a->boolean == b->boolean)
+					return 0;
+				return (a->boolean > b->boolean) ? 1 : -1;
+			case jbvNumeric:
+				return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+														 PointerGetDatum(a->numeric),
+														 PointerGetDatum(b->numeric)));
+			case jbvArray:
+				if (a->array.nelems == b->array.nelems)
+				{
+					int i, r;
+
+					for(i=0; i<a->array.nelems; i++)
+						if ((r = compareJsonbValue(a->array.elems + i, 
+												   b->array.elems + i)) != 0)
+							return r;
+
+					return 0;
+				}
+
+				return (a->array.nelems > b->array.nelems) ? 1 : -1;
+			case jbvHash:
+				if (a->hash.npairs == b->hash.npairs)
+				{
+					int i, r;
+
+					for(i=0; i<a->hash.npairs; i++)
+					{
+						if ((r = compareJsonbStringValue(&a->hash.pairs[i].key,
+														 &b->hash.pairs[i].key,
+														 NULL)) != 0)
+							return r;
+						if ((r = compareJsonbValue(&a->hash.pairs[i].value, 
+												   &b->hash.pairs[i].value)) != 0)
+							return r;
+					}
+
+					return 0;
+				}
+
+				return (a->hash.npairs > b->hash.npairs) ? 1 : -1;
+			case jbvBinary:
+				return compareJsonbBinaryValue(a->binary.data, b->binary.data);
+			default:
+				elog(PANIC, "unknown JsonbValue->type: %d", a->type);
+		}
+	}
+
+	return (a->type > b->type) ? 1 : -1;
+}
+
+int
+compareJsonbBinaryValue(char *a, char *b)
+{
+	JsonbIterator	*it1, *it2;
+	int				res = 0;
+
+	it1 = JsonbIteratorInit(a);
+	it2 = JsonbIteratorInit(b);
+
+	while(res == 0)
+	{
+		JsonbValue		v1, v2;
+		int				r1, r2;
+
+		r1 = JsonbIteratorGet(&it1, &v1, false);
+		r2 = JsonbIteratorGet(&it2, &v2, false);
+
+		if (r1 == r2)
+		{
+			if (r1 == 0)
+				break; /* equal */
+
+			if (v1.type == v2.type)
+			{
+				switch(v1.type)
+				{
+					case jbvString:
+						res = compareJsonbStringValue(&v1, &v2, NULL);
+						break;
+					case jbvBool:
+						if (v1.boolean == v2.boolean)
+							res = 0;
+						else
+							res = (v1.boolean > v2.boolean) ? 1 : -1;
+						break;
+					case jbvNumeric:
+						res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+												 PointerGetDatum(v1.numeric),
+												 PointerGetDatum(v2.numeric)));
+						break;
+					case jbvArray:
+						if (v1.array.nelems != v2.array.nelems)
+							res = (v1.array.nelems > v2.array.nelems) ? 1 : -1;
+						break;
+					case jbvHash:
+						if (v1.hash.npairs != v2.hash.npairs)
+							res = (v1.hash.npairs > v2.hash.npairs) ? 1 : -1;
+						break;
+					default:
+						break;
+				}
+			}
+			else
+			{
+				res = (v1.type > v2.type) ?  1 : -1; /* dummy order */
+			}
+		}
+		else
+		{
+			res = (r1 > r2) ? 1 : -1; /* dummy order */
+		}
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *                         find string key in hash or array                 *
+ ****************************************************************************/
+JsonbValue*
+findUncompressedJsonbValueByValue(char *buffer, uint32 flags, uint32 *lowbound, JsonbValue *key)
+{
+	uint32				header = *(uint32*)buffer;
+	static JsonbValue 	r;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) != 
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		JEntry	*array = (JEntry*)(buffer + sizeof(header));
+		char 	*data = (char*)(array + (header & JB_COUNT_MASK));
+		int 	i;
+
+		for(i=(lowbound) ? *lowbound : 0; i<(header & JB_COUNT_MASK); i++) {
+			JEntry	*e = array + i;
+
+			if (JBE_ISNULL(*e) && key->type == jbvNull)
+			{
+				r.type = jbvNull;
+				if (lowbound)
+					*lowbound = i;
+				r.size = sizeof(JEntry);
+
+				return &r;
+			}
+			else if (JBE_ISSTRING(*e) && key->type == jbvString )
+			{
+				if (key->string.len == JBE_LEN(*e) &&
+					memcmp(key->string.val, data + JBE_OFF(*e), 
+						   key->string.len) == 0)
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*e);
+					r.string.len = key->string.len;
+					r.size = sizeof(JEntry) + r.string.len;
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISBOOL(*e) && key->type == jbvBool)
+			{
+				if ((JBE_ISBOOL_TRUE(*e) && key->boolean == true) ||
+					(JBE_ISBOOL_FALSE(*e) && key->boolean == false))
+				{
+					r = *key;
+					r.size = sizeof(JEntry);
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISNUMERIC(*e) && key->type == jbvNumeric)
+			{
+				if (DatumGetBool(DirectFunctionCall2(numeric_eq, 
+								 PointerGetDatum(data + INTALIGN(JBE_OFF(*e))),
+								 PointerGetDatum(key->numeric))) == true)
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*e)));
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+		}
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		JEntry  *array = (JEntry*)(buffer + sizeof(header));
+		char    *data = (char*)(array + (header & JB_COUNT_MASK) * 2);
+		uint32	stopLow = lowbound ? *lowbound : 0,
+				stopHigh = (header & JB_COUNT_MASK),
+				stopMiddle;
+
+		if (key->type != jbvString)
+			return NULL;
+
+		while (stopLow < stopHigh)
+		{
+			int		difference;
+			JEntry	*e;
+
+			stopMiddle = stopLow + (stopHigh - stopLow) / 2;
+
+			e = array + stopMiddle * 2;
+
+			if (key->string.len == JBE_LEN(*e))
+				difference = memcmp(data + JBE_OFF(*e), key->string.val,
+									key->string.len);
+			else
+				difference = (JBE_LEN(*e) > key->string.len) ? 1 : -1;
+
+			if (difference == 0)
+			{
+				JEntry	*v = e + 1;
+
+				if (lowbound)
+					*lowbound = stopMiddle + 1;
+
+				if (JBE_ISSTRING(*v))
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*v);
+					r.string.len = JBE_LEN(*v);
+					r.size = sizeof(JEntry) + r.string.len;
+				}
+				else if (JBE_ISBOOL(*v))
+				{
+					r.type = jbvBool;
+					r.boolean = (JBE_ISBOOL_TRUE(*v)) ? true : false;
+					r.size = sizeof(JEntry);
+				}
+				else if (JBE_ISNUMERIC(*v))
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*v)));
+					r.size = 2*sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+				}
+				else if (JBE_ISNULL(*v))
+				{
+					r.type = jbvNull;
+					r.size = sizeof(JEntry);
+				}
+				else
+				{
+					r.type = jbvBinary;
+					r.binary.data = data + INTALIGN(JBE_OFF(*v));
+					r.binary.len = JBE_LEN(*v) - 
+									(INTALIGN(JBE_OFF(*v)) - JBE_OFF(*v));
+					r.size = 2*sizeof(JEntry) + r.binary.len;
+				}
+
+				return &r;
+			}
+			else if (difference < 0)
+			{
+				stopLow = stopMiddle + 1;
+			}
+			else
+			{
+				stopHigh = stopMiddle;
+			}
+		}
+
+		if (lowbound)
+			*lowbound = stopLow;
+	}
+
+	return NULL;
+}
+
+JsonbValue*
+findUncompressedJsonbValue(char *buffer, uint32 flags, uint32 *lowbound,
+						   char *key, uint32 keylen)
+{
+	JsonbValue	v;
+
+	if (key == NULL)
+	{
+		v.type = jbvNull;
+	}
+	else
+	{
+		v.type = jbvString;
+		v.string.val = key;
+		v.string.len = keylen;
+	}
+
+	return findUncompressedJsonbValueByValue(buffer, flags, lowbound, &v);
+}
+
+JsonbValue*
+getJsonbValue(char *buffer, uint32 flags, int32 i)
+{
+	uint32				header = *(uint32*)buffer;
+	static JsonbValue	r;
+	JEntry				*array, *e;
+	char				*data;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) !=
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (i >= 0)
+	{
+		if (i >= (header & JB_COUNT_MASK))
+			return NULL;
+	}
+	else
+	{
+		if (-i > (header & JB_COUNT_MASK))
+			return NULL;
+
+		i = (header & JB_COUNT_MASK) + i;
+	}
+
+	array = (JEntry*)(buffer + sizeof(header));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		e = array + i;
+		data = (char*)(array + (header & JB_COUNT_MASK));
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		e = array + i * 2 + 1;
+		data = (char*)(array + (header & JB_COUNT_MASK) * 2);
+	}
+	else
+	{
+		return NULL;
+	}
+
+	if (JBE_ISSTRING(*e))
+	{
+		r.type = jbvString;
+		r.string.val = data + JBE_OFF(*e);
+		r.string.len = JBE_LEN(*e);
+		r.size = sizeof(JEntry) + r.string.len;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		r.type = jbvBool;
+		r.boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		r.size = sizeof(JEntry);
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		r.type = jbvNumeric;
+		r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*e)));
+		r.size = 2*sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		r.type = jbvNull;
+		r.size = sizeof(JEntry);
+	}
+	else
+	{
+		r.type = jbvBinary;
+		r.binary.data = data + INTALIGN(JBE_OFF(*e));
+		r.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		r.size = r.binary.len + 2*sizeof(JEntry);
+	}
+
+	return &r;
+}
+
+/****************************************************************************
+ *                         Walk on tree representation of jsonb             *
+ ****************************************************************************/
+static void
+walkUncompressedJsonbDo(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg, uint32 level) 
+{
+	int i;
+
+	switch(v->type)
+	{
+		case jbvArray:
+			cb(cb_arg, v, WJB_BEGIN_ARRAY, level);
+			for(i=0; i<v->array.nelems; i++)
+			{
+				if (v->array.elems[i].type == jbvNull ||
+					v->array.elems[i].type == jbvString ||
+					v->array.elems[i].type == jbvBool ||
+					v->array.elems[i].type == jbvNumeric ||
+					v->array.elems[i].type == jbvBinary)
+					cb(cb_arg, v->array.elems + i, WJB_ELEM, level);
+				else
+					walkUncompressedJsonbDo(v->array.elems + i, cb, cb_arg,
+											level + 1);
+			}
+			cb(cb_arg, v, WJB_END_ARRAY, level);
+			break;
+		case jbvHash:
+			cb(cb_arg, v, WJB_BEGIN_OBJECT, level);
+
+			for(i=0; i<v->hash.npairs; i++)
+			{
+				cb(cb_arg, &v->hash.pairs[i].key, WJB_KEY, level);
+				
+				if (v->hash.pairs[i].value.type == jbvNull ||
+					v->hash.pairs[i].value.type == jbvString ||
+					v->hash.pairs[i].value.type == jbvBool ||
+					v->hash.pairs[i].value.type == jbvNumeric ||
+					v->hash.pairs[i].value.type == jbvBinary)
+					cb(cb_arg, &v->hash.pairs[i].value, WJB_VALUE, level);
+				else 
+					walkUncompressedJsonbDo(&v->hash.pairs[i].value, cb, cb_arg,
+											level + 1);
+			}
+
+			cb(cb_arg, v, WJB_END_OBJECT, level);
+			break;
+		default:
+			elog(PANIC, "impossible JsonbValue->type: %d", v->type);
+	}
+}
+
+void
+walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg)
+{
+	if (v)
+		walkUncompressedJsonbDo(v, cb, cb_arg, 0);
+}
+
+/****************************************************************************
+ *                         Iteration over binary jsonb                      *
+ ****************************************************************************/
+static void
+parseBuffer(JsonbIterator *it, char *buffer)
+{
+	uint32	header = *(uint32*)buffer;
+
+	it->type = header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT);
+	it->nelems = header & JB_COUNT_MASK;
+	it->buffer = buffer;
+
+
+	buffer += sizeof(uint32);
+	it->array = (JEntry*)buffer;
+
+	it->state = jbi_start;
+
+	switch(it->type)
+	{
+		case JB_FLAG_ARRAY:
+			it->data = buffer + it->nelems * sizeof(JEntry);
+			it->isScalar = (header & JB_FLAG_SCALAR) ? true : false;
+			Assert(it->isScalar == false || it->nelems == 1);
+			break;
+		case JB_FLAG_OBJECT:
+			it->data = buffer + it->nelems * sizeof(JEntry) * 2;
+			break;
+		default:
+			elog(PANIC, "impossible type: %08x", it->type);
+	}
+}
+
+JsonbIterator*
+JsonbIteratorInit(char *buffer)
+{
+	JsonbIterator	*it = palloc(sizeof(*it));
+
+	parseBuffer(it, buffer);
+	it->next = NULL;
+
+	return it;
+}
+
+static bool
+formAnswer(JsonbIterator **it, JsonbValue *v, JEntry *e, bool skipNested)
+{
+	if (JBE_ISSTRING(*e))
+	{
+		v->type = jbvString;
+		v->string.val = (*it)->data + JBE_OFF(*e);
+		v->string.len = JBE_LEN(*e);
+		v->size = sizeof(JEntry) + v->string.len;
+
+		return false;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		v->type = jbvBool;
+		v->boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		v->type = jbvNumeric;
+		v->numeric = (Numeric)((*it)->data + INTALIGN(JBE_OFF(*e)));
+		v->size = 2*sizeof(JEntry) + VARSIZE_ANY(v->numeric);
+
+		return false;
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (skipNested)
+	{
+		v->type = jbvBinary;
+		v->binary.data = (*it)->data + INTALIGN(JBE_OFF(*e));
+		v->binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		v->size = v->binary.len + 2*sizeof(JEntry);
+
+		return false;
+	}
+	else
+	{
+		JsonbIterator *nit = palloc(sizeof(*nit));
+
+		parseBuffer(nit, (*it)->data + INTALIGN(JBE_OFF(*e)));
+		nit->next = *it;
+		*it = nit;
+
+		return true;
+	}
+}
+
+static JsonbIterator*
+up(JsonbIterator *it)
+{
+	JsonbIterator *v = it->next;
+
+	pfree(it);
+
+	return v;
+}
+
+int
+JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested)
+{
+	int res;
+
+	if (*it == NULL)
+		return 0;
+
+	/*
+	 * Encode all possible states by one integer. That's possible
+	 * because enum members of JsonbIterator->state uses different bits
+	 * than JB_FLAG_ARRAY/JB_FLAG_OBJECT. See definition of JsonbIterator
+	 */
+
+	switch((*it)->type | (*it)->state)
+	{
+		case JB_FLAG_ARRAY | jbi_start:
+			(*it)->state = jbi_elem;
+			(*it)->i = 0;
+			v->type = jbvArray;
+			v->array.nelems = (*it)->nelems;
+			res = WJB_BEGIN_ARRAY;
+			v->array.scalar = (*it)->isScalar;
+			break;
+		case JB_FLAG_ARRAY | jbi_elem:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_ARRAY;
+			}
+			else if (formAnswer(it, v, &(*it)->array[ (*it)->i++ ], skipNested))
+			{
+				res = JsonbIteratorGet(it, v, skipNested);
+			}
+			else
+			{
+				res = WJB_ELEM;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_start:
+			(*it)->state = jbi_key;
+			(*it)->i = 0;
+			v->type = jbvHash;
+			v->hash.npairs = (*it)->nelems;
+			res = WJB_BEGIN_OBJECT;
+			break;
+		case JB_FLAG_OBJECT | jbi_key:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_OBJECT;
+			}
+			else
+			{
+				formAnswer(it, v, &(*it)->array[ (*it)->i * 2 ], false);
+				(*it)->state = jbi_value;
+				res = WJB_KEY;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_value:
+			(*it)->state = jbi_key;
+			if (formAnswer(it, v, &(*it)->array[ ((*it)->i++) * 2 + 1], skipNested))
+				res = JsonbIteratorGet(it, v, skipNested);
+			else
+				res = WJB_VALUE;
+			break;
+		default:
+			elog(PANIC,"unknown state %08x", (*it)->type & (*it)->state);
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *        Transformation from tree to binary representation of jsonb        *
+ ****************************************************************************/
+typedef struct CompressState
+{
+	char	*begin;
+	char	*ptr;
+
+	struct {
+		uint32	i;
+		uint32	*header;
+		JEntry	*array;
+		char	*begin;
+	} *levelstate, *lptr, *pptr;
+
+	uint32	maxlevel;
+	
+} CompressState;
+
+#define	curLevelState	state->lptr
+#define prevLevelState	state->pptr
+
+static void
+putJEntryString(CompressState *state, JsonbValue* value, uint32 level, uint32 i)
+{
+	curLevelState = state->levelstate + level;
+
+	if (i == 0)
+		curLevelState->array[0].entry = JENTRY_ISFIRST;
+	else
+		curLevelState->array[i].entry = 0;
+
+	switch(value->type)
+	{
+		case jbvNull:
+			curLevelState->array[i].entry |= JENTRY_ISNULL;
+
+			if (i>0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvString:
+			memcpy(state->ptr, value->string.val, value->string.len);
+			state->ptr += value->string.len;
+
+			if (i == 0)
+				curLevelState->array[i].entry |= value->string.len;
+			else
+				curLevelState->array[i].entry |= 
+					(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+					value->string.len;
+			break;
+		case jbvBool:
+			curLevelState->array[i].entry |= (value->boolean) ?
+				JENTRY_ISTRUE : JENTRY_ISFALSE;
+
+			if (i>0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvNumeric:
+			{
+				int addlen = INTALIGN(state->ptr - state->begin) -
+								(state->ptr - state->begin);
+				int	numlen = VARSIZE_ANY(value->numeric); 
+
+				switch(addlen)
+				{
+					case 3:
+						*state->ptr = '\0'; state->ptr++;
+					case 2:
+						*state->ptr = '\0'; state->ptr++;
+					case 1:
+						*state->ptr = '\0'; state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->numeric, numlen);
+				state->ptr += numlen;
+
+				curLevelState->array[i].entry |= JENTRY_ISNUMERIC;
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + numlen;
+				else
+					curLevelState->array[i].entry |= 
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + numlen;
+				break;
+			}
+		case jbvBinary:
+			{
+				int addlen = INTALIGN(state->ptr - state->begin) -
+								(state->ptr - state->begin);
+
+				switch(addlen)
+				{
+					case 3:
+						*state->ptr = '\0'; state->ptr++;
+					case 2:
+						*state->ptr = '\0'; state->ptr++;
+					case 1:
+						*state->ptr = '\0'; state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->binary.data, value->binary.len);
+				state->ptr += value->binary.len;
+
+				curLevelState->array[i].entry |= JENTRY_ISNEST;
+
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + value->binary.len;
+				else
+					curLevelState->array[i].entry |=
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + value->binary.len;
+			}
+			break;
+		default:
+			elog(PANIC,"Unsupported JsonbValue type: %d", value->type);
+	}
+}
+
+static void
+compressCallback(void *arg, JsonbValue* value, uint32 flags, uint32 level)
+{
+	CompressState	*state = arg;
+
+	if (level == state->maxlevel) {
+		state->maxlevel *= 2;
+		state->levelstate = repalloc(state->levelstate,
+								 sizeof(*state->levelstate) * state->maxlevel);
+	}
+
+	curLevelState = state->levelstate + level;
+
+	if (flags & (WJB_BEGIN_ARRAY | WJB_BEGIN_OBJECT))
+	{
+		Assert(((flags & WJB_BEGIN_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_BEGIN_OBJECT) && value->type == jbvHash));
+
+		curLevelState->begin = state->ptr;
+
+		switch(INTALIGN(state->ptr - state->begin) -
+			   (state->ptr - state->begin))
+		{
+			case 3:
+				*state->ptr = '\0'; state->ptr++;
+			case 2:
+				*state->ptr = '\0'; state->ptr++;
+			case 1:
+				*state->ptr = '\0'; state->ptr++;
+			case 0:
+			default:
+				break;
+		}
+
+		curLevelState->header = (uint32*)state->ptr;
+		state->ptr += sizeof(*curLevelState->header);
+
+		curLevelState->array = (JEntry*)state->ptr;
+		curLevelState->i = 0;
+
+		if (value->type == jbvArray)
+		{
+			*curLevelState->header = value->array.nelems | JB_FLAG_ARRAY ;
+			state->ptr += sizeof(JEntry) * value->array.nelems;
+
+			if (value->array.scalar)
+			{
+				Assert(value->array.nelems == 1);
+				Assert(level == 0);
+				*curLevelState->header |= JB_FLAG_SCALAR;
+			}
+		}
+		else
+		{
+			*curLevelState->header = value->hash.npairs | JB_FLAG_OBJECT ;
+			state->ptr += sizeof(JEntry) * value->hash.npairs * 2;
+		}
+	}
+	else if (flags & WJB_ELEM)
+	{
+		putJEntryString(state, value, level, curLevelState->i);
+		curLevelState->i++;
+	}
+	else if (flags & WJB_KEY)
+	{
+		Assert(value->type == jbvString);
+
+		putJEntryString(state, value, level, curLevelState->i * 2);
+	}
+	else if (flags & WJB_VALUE)
+	{
+		putJEntryString(state, value, level, curLevelState->i * 2 + 1);
+		curLevelState->i++;
+	}
+	else if (flags & (WJB_END_ARRAY | WJB_END_OBJECT))
+	{
+		uint32	len, i;
+
+		Assert(((flags & WJB_END_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_END_OBJECT) && value->type == jbvHash));
+		if (level == 0)
+			return;
+
+		len = state->ptr - (char*)curLevelState->begin;
+
+		prevLevelState = curLevelState - 1;
+
+		if (*prevLevelState->header & JB_FLAG_ARRAY) {
+			i = prevLevelState->i;
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			if (i == 0)
+				prevLevelState->array[0].entry |= JENTRY_ISFIRST | len;
+			else
+				prevLevelState->array[i].entry |=
+					(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else if (*prevLevelState->header & JB_FLAG_OBJECT)
+		{
+			i = 2 * prevLevelState->i + 1; /* VALUE, not a KEY */
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			prevLevelState->array[i].entry |=
+				(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else
+		{
+			elog(PANIC, "Wrong parent");
+		}
+
+		Assert(state->ptr - curLevelState->begin <= value->size);
+		prevLevelState->i++;
+	}
+	else
+	{
+		elog(PANIC, "Wrong flags");
+	}
+}
+
+uint32
+compressJsonb(JsonbValue *v, char *buffer) {
+	uint32			l = 0;
+	CompressState	state;
+
+	state.begin = state.ptr = buffer;
+	state.maxlevel = 8;
+	state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel);
+
+	walkUncompressedJsonb(v, compressCallback, &state);
+
+	l = state.ptr - buffer;
+	Assert(l <= v->size);
+
+	return l;
+}
+
+/****************************************************************************
+ *                  Iteration-like forming jsonb                            *
+ *       Note: it believes by default in already sorted keys in hash,       *
+ *     although with r == WJB_END_OBJECT and v == NULL  it will sort itself *
+ ****************************************************************************/
+static ToJsonbState*
+pushState(ToJsonbState **state)
+{
+	ToJsonbState	*ns = palloc(sizeof(*ns));
+
+	ns->next = *state;
+	return ns;
+}
+
+static void
+appendArray(ToJsonbState *state, JsonbValue *v)
+{
+	JsonbValue	*a = &state->v;
+
+	Assert(a->type == jbvArray);
+
+	if (a->array.nelems >= state->size)
+	{
+		state->size *= 2;
+		a->array.elems = repalloc(a->array.elems,
+								   sizeof(*a->array.elems) * state->size);
+	}
+
+	a->array.elems[a->array.nelems ++] = *v;
+
+	a->size += v->size;
+}
+
+static void
+appendKey(ToJsonbState *state, JsonbValue *v)
+{
+	JsonbValue	*h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	if (h->hash.npairs >= state->size)
+	{
+		state->size *= 2;
+		h->hash.pairs = repalloc(h->hash.pairs,
+									sizeof(*h->hash.pairs) * state->size);
+	}
+
+	h->hash.pairs[h->hash.npairs].key = *v;
+	h->hash.pairs[h->hash.npairs].order = h->hash.npairs;
+
+	h->size += v->size;
+}
+
+static void
+appendValue(ToJsonbState *state, JsonbValue *v)
+{
+
+	JsonbValue	*h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	h->hash.pairs[h->hash.npairs++].value = *v;
+
+	h->size += v->size;
+}
+
+
+JsonbValue*
+pushJsonbValue(ToJsonbState **state, int r /* WJB_* */, JsonbValue *v) {
+	JsonbValue	*h = NULL;
+
+	switch(r)
+	{
+		case WJB_BEGIN_ARRAY:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvArray;
+			(*state)->v.size = 3*sizeof(JEntry);
+			(*state)->v.array.nelems = 0;
+			(*state)->v.array.scalar = (v && v->array.scalar) ? true : false;
+			(*state)->size = (v && v->type == jbvArray && v->array.nelems > 0)
+								? v->array.nelems : 4;
+			(*state)->v.array.elems = palloc(sizeof(*(*state)->v.array.elems) *
+											 (*state)->size);
+			break;
+		case WJB_BEGIN_OBJECT:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvHash;
+			(*state)->v.size = 3*sizeof(JEntry);
+			(*state)->v.hash.npairs = 0;
+			(*state)->size = (v && v->type == jbvHash && v->hash.npairs > 0) ?
+									v->hash.npairs : 4;
+			(*state)->v.hash.pairs = palloc(sizeof(*(*state)->v.hash.pairs) *
+											(*state)->size);
+			break;
+		case WJB_ELEM:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric || 
+				   v->type == jbvBinary); 
+			appendArray(*state, v);
+			break;
+		case WJB_KEY:
+			Assert(v->type == jbvString);
+			appendKey(*state, v);
+			break;
+		case WJB_VALUE:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric || 
+				   v->type == jbvBinary); 
+			appendValue(*state, v);
+			break;
+		case WJB_END_OBJECT:
+			h = &(*state)->v;
+			if (v == NULL)
+				uniqueJsonbValue(h);
+		case WJB_END_ARRAY:
+			h = &(*state)->v;
+			*state = (*state)->next;
+			if (*state)
+			{
+				switch((*state)->v.type)
+				{
+					case jbvArray:
+						appendArray(*state, h);
+						break;
+					case jbvHash:
+						appendValue(*state, h);
+						break;
+					default:
+						elog(PANIC, "wrong parent type: %d", (*state)->v.type);
+				}
+			}
+			break;
+		default:
+			elog(PANIC, "wrong type: %08x", r);
+	}
+
+	return h;
+}
+
+/*
+ * JsonbStringIsNumber
+ * 		fast and cheap check of string
+ */
+bool
+JsonbStringIsNumber(char *string, int len) {
+    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')
+                    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;
+}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 90fa447..7f4bd1b 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -27,6 +27,7 @@
 #include "utils/builtins.h"
 #include "utils/hsearch.h"
 #include "utils/json.h"
+#include "utils/jsonb.h"
 #include "utils/jsonapi.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -63,6 +64,7 @@ static inline Datum each_worker(PG_FUNCTION_ARGS, bool as_text);
 /* semantic action functions for json_each */
 static void each_object_field_start(void *state, char *fname, bool isnull);
 static void each_object_field_end(void *state, char *fname, bool isnull);
+static void each_object_field_end_jsonb(void *state, char *fname, bool isnull);
 static void each_array_start(void *state);
 static void each_scalar(void *state, char *token, JsonTokenType tokentype);
 
@@ -70,6 +72,7 @@ static void each_scalar(void *state, char *token, JsonTokenType tokentype);
 static void elements_object_start(void *state);
 static void elements_array_element_start(void *state, bool isnull);
 static void elements_array_element_end(void *state, bool isnull);
+static void elements_array_element_end_jsonb(void *state, bool isnull);
 static void elements_scalar(void *state, char *token, JsonTokenType tokentype);
 
 /* turn a json object into a hash table */
@@ -218,12 +221,86 @@ typedef struct PopulateRecordsetState
  *
  * This SRF operates in value-per-call mode. It processes the
  * object during the first call, and the keys are simply stashed
- * in an array, whise size is expanded as necessary. This is probably
+ * in an array, whose size is expanded as necessary. This is probably
  * safe enough for a list of keys of a single object, since they are
  * limited in size to NAMEDATALEN and the number of keys is unlikely to
  * be so huge that it has major memory implications.
  */
 
+Datum
+jsonb_object_keys(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	OkeysState *state;
+	int			i;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext    oldcontext;
+		Jsonb           *jb = PG_GETARG_JSONB(0);
+		bool             skipNested = false;
+		JsonbIterator   *it;
+		JsonbValue	     v;
+		int              r = 0;
+
+	 
+		if (JB_ROOT_IS_SCALAR(jb))
+			elog(ERROR,"Cannot call jsonb_object_keys on a scalar");
+		else if (JB_ROOT_IS_ARRAY(jb))
+			elog(ERROR,"Cannot call jsonb_object_keys on an array");
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		state = palloc(sizeof(OkeysState));
+
+		state->result_size = JB_ROOT_COUNT(jb);
+		state->result_count = 0;
+		state->sent_count = 0;
+		state->result = palloc(state->result_size * sizeof(char *));
+
+		it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+		while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+		{
+			skipNested = true;
+			
+			if (r == WJB_KEY)
+			{
+				char * cstr;
+
+				cstr = palloc(v.string.len+1 * sizeof(char));
+				memcpy(cstr,v.string.val, v.string.len);
+				cstr[v.string.len] = '\0';
+				state->result[state->result_count++] = cstr;
+			}
+		}
+
+
+		MemoryContextSwitchTo(oldcontext);
+		funcctx->user_fctx = (void *) state;
+
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	state = (OkeysState *) funcctx->user_fctx;
+
+	if (state->sent_count < state->result_count)
+	{
+		char	   *nxt = state->result[state->sent_count++];
+
+		SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
+	}
+
+	/* cleanup to reduce or eliminate memory leaks */
+	for (i = 0; i < state->result_count; i++)
+		pfree(state->result[i]);
+	pfree(state->result);
+	pfree(state);
+
+	SRF_RETURN_DONE(funcctx);
+}
+
 
 Datum
 json_object_keys(PG_FUNCTION_ARGS)
@@ -234,12 +311,26 @@ json_object_keys(PG_FUNCTION_ARGS)
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		text	   *json = PG_GETARG_TEXT_P(0);
-		JsonLexContext *lex = makeJsonLexContext(json, true);
+		text	   *json; //  = PG_GETARG_TEXT_P(0);
+		JsonLexContext *lex; //  = makeJsonLexContext(json, true);
 		JsonSemAction *sem;
 
 		MemoryContext oldcontext;
 
+		if (get_fn_expr_argtype(fcinfo->flinfo, 0) == JSONOID)
+		{
+			/* just get the text */
+			json = PG_GETARG_TEXT_P(0);
+		}
+		else
+		{
+			Jsonb      *jb = PG_GETARG_JSONB(0);
+			
+			json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+		}
+
+		lex = makeJsonLexContext(json, true);
+
 		funcctx = SRF_FIRSTCALL_INIT();
 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
@@ -359,6 +450,26 @@ json_object_field(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	text       *jsontext;
+	text	   *result;
+	text	   *fname = PG_GETARG_TEXT_P(1);
+	char	   *fnamestr = text_to_cstring(fname);
+
+	jsontext = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	
+	result = get_worker(jsontext, fnamestr, -1, NULL, NULL, -1, false);
+
+	if (result != NULL)
+		PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	else
+		PG_RETURN_NULL();
+
+}
+
+Datum
 json_object_field_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -375,6 +486,26 @@ json_object_field_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field_text(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	text       *jsontext;
+	text	   *result;
+	text	   *fname = PG_GETARG_TEXT_P(1);
+	char	   *fnamestr = text_to_cstring(fname);
+
+	jsontext = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+
+	result = get_worker(jsontext, fnamestr, -1, NULL, NULL, -1, true);
+	
+	if (result != NULL)
+		PG_RETURN_TEXT_P(result);
+	else
+		PG_RETURN_NULL();
+
+}
+
+Datum
 json_array_element(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -390,6 +521,24 @@ json_array_element(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	text       *jsontext;
+	text	   *result;
+	int			element = PG_GETARG_INT32(1);
+
+	jsontext = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	
+	result = get_worker(jsontext, NULL, element, NULL, NULL, -1, false);
+
+	if (result != NULL)
+		PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	else
+		PG_RETURN_NULL();
+}
+
+Datum
 json_array_element_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -405,24 +554,55 @@ json_array_element_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element_text(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	text       *jsontext;
+	text	   *result;
+	int			element = PG_GETARG_INT32(1);
+
+	jsontext = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	
+	result = get_worker(jsontext, NULL, element, NULL, NULL, -1, true);
+
+	if (result != NULL)
+		PG_RETURN_TEXT_P(result);
+	else
+		PG_RETURN_NULL();
+}
+
+Datum
 json_extract_path(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, false);
 }
 
 Datum
+jsonb_extract_path(PG_FUNCTION_ARGS)
+{
+	return get_path_all(fcinfo, false);
+}
+
+Datum
 json_extract_path_text(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, true);
 }
 
+Datum
+jsonb_extract_path_text(PG_FUNCTION_ARGS)
+{
+	return get_path_all(fcinfo, true);
+}
+
 /*
  * common routine for extract_path functions
  */
 static inline Datum
 get_path_all(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
 	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
 	text	   *result;
 	Datum	   *pathtext;
@@ -434,6 +614,19 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	long		ind;
 	char	   *endptr;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
 	if (array_contains_nulls(path))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -472,9 +665,17 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	result = get_worker(json, NULL, -1, tpath, ipath, npath, as_text);
 
 	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
+	{
+		if (val_type == JSONOID || as_text)
+			PG_RETURN_TEXT_P(result);
+		else
+			PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	}
 	else
+	{
+		/* null is null regardless */
 		PG_RETURN_NULL();
+	}
 }
 
 /*
@@ -814,12 +1015,14 @@ get_scalar(void *state, char *token, JsonTokenType tokentype)
 Datum
 json_array_length(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
 	AlenState  *state;
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(AlenState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -839,6 +1042,24 @@ json_array_length(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(state->count);
 }
 
+Datum
+jsonb_array_length(PG_FUNCTION_ARGS)
+{
+	Jsonb *jb = PG_GETARG_JSONB(0);
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a scalar")));
+	else if (! JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a non-array")));
+
+	PG_RETURN_INT32(JB_ROOT_COUNT(jb));
+	
+}
+
 /*
  * These next two check ensure that the json is an array (since it can't be
  * a scalar or an object).
@@ -895,22 +1116,49 @@ json_each(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_each(PG_FUNCTION_ARGS)
+{
+	return each_worker(fcinfo, false);
+}
+
+Datum
 json_each_text(PG_FUNCTION_ARGS)
 {
 	return each_worker(fcinfo, true);
 }
 
+Datum
+jsonb_each_text(PG_FUNCTION_ARGS)
+{
+	return each_worker(fcinfo, true);
+}
+
 static inline Datum
 each_worker(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	JsonLexContext *lex = makeJsonLexContext(json, true);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	TupleDesc	tupdesc;
 	EachState  *state;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
+	lex = makeJsonLexContext(json, true);
 	state = palloc0(sizeof(EachState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -944,7 +1192,10 @@ each_worker(PG_FUNCTION_ARGS, bool as_text)
 	sem->array_start = each_array_start;
 	sem->scalar = each_scalar;
 	sem->object_field_start = each_object_field_start;
-	sem->object_field_end = each_object_field_end;
+	if (val_type == JSONOID)
+		sem->object_field_end = each_object_field_end;
+	else
+		sem->object_field_end = each_object_field_end_jsonb;
 
 	state->normalize_results = as_text;
 	state->next_scalar = false;
@@ -1033,6 +1284,61 @@ each_object_field_end(void *state, char *fname, bool isnull)
 }
 
 static void
+each_object_field_end_jsonb(void *state, char *fname, bool isnull)
+{
+	EachState  *_state = (EachState *) state;
+	MemoryContext old_cxt;
+	int			len;
+	Jsonb	   *val;
+	HeapTuple	tuple;
+	Datum		values[2];
+	bool		nulls[2] = {false, false};
+
+	/* skip over nested objects */
+	if (_state->lex->lex_level != 1)
+		return;
+
+	/* use the tmp context so we can clean up after each tuple is done */
+	old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
+
+	values[0] = CStringGetTextDatum(fname);
+
+	if (isnull && _state->normalize_results)
+	{
+		nulls[1] = true;
+		values[1] = (Datum) NULL;
+	}
+	else if (_state->next_scalar)
+	{
+		values[1] = CStringGetTextDatum(_state->normalized_scalar);
+		_state->next_scalar = false;
+	}
+	else if (_state->normalize_results)
+	{
+		len = _state->lex->prev_token_terminator - _state->result_start;
+		values[1] = PointerGetDatum(cstring_to_text_with_len(_state->result_start, len));
+	}
+	else
+	{
+		char *cstr;
+		len = _state->lex->prev_token_terminator - _state->result_start;
+		cstr = palloc(len+1 * sizeof(char));
+		memcpy(cstr,_state->result_start,len);
+		cstr[len] = '\0';
+		val = DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(cstr)));
+		values[1] = PointerGetDatum(val);
+	}
+
+	tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
+
+	tuplestore_puttuple(_state->tuple_store, tuple);
+
+	/* clean up and switch back */
+	MemoryContextSwitchTo(old_cxt);
+	MemoryContextReset(_state->tmp_cxt);
+}
+
+static void
 each_array_start(void *state)
 {
 	EachState  *_state = (EachState *) state;
@@ -1067,19 +1373,41 @@ each_scalar(void *state, char *token, JsonTokenType tokentype)
  *
  * a lot of this processing is similar to the json_each* functions
  */
+
+Datum
+jsonb_array_elements(PG_FUNCTION_ARGS)
+{
+	return json_array_elements(fcinfo);
+}
+
 Datum
 json_array_elements(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
 
-	/* elements doesn't need any escaped strings, so use false here */
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	TupleDesc	tupdesc;
 	ElementsState *state;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
+	/* elements doesn't need any escaped strings, so use false here */
+	lex  = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(ElementsState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -1114,7 +1442,10 @@ json_array_elements(PG_FUNCTION_ARGS)
 	sem->object_start = elements_object_start;
 	sem->scalar = elements_scalar;
 	sem->array_element_start = elements_array_element_start;
-	sem->array_element_end = elements_array_element_end;
+	if (val_type == JSONOID)
+		sem->array_element_end = elements_array_element_end;
+	else
+		sem->array_element_end = elements_array_element_end_jsonb;
 
 	state->lex = lex;
 	state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
@@ -1174,6 +1505,41 @@ elements_array_element_end(void *state, bool isnull)
 }
 
 static void
+elements_array_element_end_jsonb(void *state, bool isnull)
+{
+	ElementsState *_state = (ElementsState *) state;
+	MemoryContext old_cxt;
+	int			len;
+	Jsonb      *jbval;
+	HeapTuple	tuple;
+	Datum		values[1];
+	static bool nulls[1] = {false};
+	char *cstr;
+
+	/* skip over nested objects */
+	if (_state->lex->lex_level != 1)
+		return;
+
+	/* use the tmp context so we can clean up after each tuple is done */
+	old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
+
+	len = _state->lex->prev_token_terminator - _state->result_start;
+	cstr = palloc(len+1 * sizeof(char));
+	memcpy(cstr,_state->result_start,len);
+	cstr[len] = '\0';
+	jbval = DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(cstr)));
+	values[0] = PointerGetDatum(jbval);
+
+	tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
+
+	tuplestore_puttuple(_state->tuple_store, tuple);
+
+	/* clean up and switch back */
+	MemoryContextSwitchTo(old_cxt);
+	MemoryContextReset(_state->tmp_cxt);
+}
+
+static void
 elements_object_start(void *state)
 {
 	ElementsState *_state = (ElementsState *) state;
@@ -1214,9 +1580,16 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
  * field in the record is then looked up by name.
  */
 Datum
+jsonb_populate_record(PG_FUNCTION_ARGS)
+{
+	return json_populate_record(fcinfo);
+}
+
+Datum
 json_populate_record(PG_FUNCTION_ARGS)
 {
 	Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	Oid         jtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
 	text	   *json;
 	bool		use_json_as_text;
 	HTAB	   *json_hash;
@@ -1234,6 +1607,8 @@ json_populate_record(PG_FUNCTION_ARGS)
 	char		fname[NAMEDATALEN];
 	JsonHashEntry *hashentry;
 
+	Assert(jtype == JSONOID || jtype == JSONBOID);
+
 	use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
 
 	if (!type_is_rowtype(argtype))
@@ -1268,7 +1643,17 @@ json_populate_record(PG_FUNCTION_ARGS)
 		tupTypmod = HeapTupleHeaderGetTypMod(rec);
 	}
 
-	json = PG_GETARG_TEXT_P(1);
+	if (jtype == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(1);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(1);
+		
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
 
 	json_hash = get_json_object_as_hash(json, "json_populate_record", use_json_as_text);
 
@@ -1559,9 +1944,17 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
  * per object in the array.
  */
 Datum
+jsonb_populate_recordset(PG_FUNCTION_ARGS)
+{
+	return json_populate_recordset(fcinfo);
+}
+
+
+Datum
 json_populate_recordset(PG_FUNCTION_ARGS)
 {
 	Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	Oid         jtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
 	text	   *json;
 	bool		use_json_as_text;
 	ReturnSetInfo *rsi;
@@ -1621,7 +2014,17 @@ json_populate_recordset(PG_FUNCTION_ARGS)
 	if (PG_ARGISNULL(1))
 		PG_RETURN_NULL();
 
-	json = PG_GETARG_TEXT_P(1);
+	if (jtype == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(1);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(1);
+		
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
 
 	if (PG_ARGISNULL(0))
 		rec = NULL;
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index ae71bd6..eb37600 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -169,6 +169,8 @@ BuildEventTriggerCache(void)
 			event = EVT_DDLCommandEnd;
 		else if (strcmp(evtevent, "sql_drop") == 0)
 			event = EVT_SQLDrop;
+		else if (strcmp(evtevent, "transaction_commit") == 0)
+			event = EVT_Commit;
 		else
 			continue;
 
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 6aa4890..143a451 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1753,6 +1753,18 @@ DATA(insert OID = 3966 (  "#>"	   PGNSP PGUID b f f 114 1009 114 0 0 json_extrac
 DESCR("get value from json with path elements");
 DATA(insert OID = 3967 (  "#>>"    PGNSP PGUID b f f 114 1009 25 0 0 json_extract_path_text_op - - ));
 DESCR("get value from json as text with path elements");
+DATA(insert OID = 3211 (  "->"	   PGNSP PGUID b f f 3802 25 3802 0 0 jsonb_object_field - - ));
+DESCR("get jsonb object field");
+DATA(insert OID = 3204 (  "->>"    PGNSP PGUID b f f 3802 25 25 0 0 jsonb_object_field_text - - ));
+DESCR("get jsonb object field as text");
+DATA(insert OID = 3212 (  "->"	   PGNSP PGUID b f f 3802 23 3802 0 0 jsonb_array_element - - ));
+DESCR("get jsonb array element");
+DATA(insert OID = 3205 (  "->>"    PGNSP PGUID b f f 3802 23 25 0 0 jsonb_array_element_text - - ));
+DESCR("get jsonb array element as text");
+DATA(insert OID = 3213 (  "#>"	   PGNSP PGUID b f f 3802 1009 3802 0 0 jsonb_extract_path_op - - ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3206 (  "#>>"    PGNSP PGUID b f f 3802 1009 25 0 0 jsonb_extract_path_text_op - - ));
+DESCR("get value from jsonb as text with path elements");
 
 
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b5b1ff7..6959ab7 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4463,6 +4463,50 @@ DESCR("I/O");
 DATA(insert OID = 3774 (  regdictionarysend PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3769" _null_ _null_ _null_ _null_ regdictionarysend _null_ _null_ _null_ ));
 DESCR("I/O");
 
+/* jsonb */
+DATA(insert OID =  3806 (  jsonb_in			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2275" _null_ _null_ _null_ _null_ jsonb_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3805 (  jsonb_recv		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2281" _null_ _null_ _null_ _null_ jsonb_recv _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3804 (  jsonb_out		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3802" _null_ _null_ _null_ _null_ jsonb_out _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3803 (  jsonb_send		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3802" _null_ _null_ _null_ _null_	jsonb_send _null_ _null_ _null_ ));
+DESCR("I/O");
+
+DATA(insert OID = 3969 (  jsonb_object_field			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field _null_ _null_ _null_ ));
+DESCR("get jsonb object field");
+DATA(insert OID = 3179 (  jsonb_object_field_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field_text _null_ _null_ _null_ ));
+DESCR("get jsonb object field as text");
+DATA(insert OID = 3180 (  jsonb_array_element		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element _null_ _null_ _null_ ));
+DESCR("get jsonb array element");
+DATA(insert OID = 3195 (  jsonb_array_element_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element_text _null_ _null_ _null_ ));
+DESCR("get jsonb array element as text");
+DATA(insert OID = 3196 (  jsonb_extract_path			PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 3802 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3199 (  jsonb_extract_path_op		PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3200 (  jsonb_extract_path_text	PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 25 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DESCR("get value from jsonb as text with path elements");
+DATA(insert OID = 3197 (  jsonb_extract_path_text_op PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 25 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DESCR("get value from jsonb as text with path elements");
+DATA(insert OID = 3198 (  jsonb_array_elements		PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 3802 "3802" "{3802,3802}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3207 (  jsonb_array_length			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "3802" _null_ _null_ _null_ _null_ jsonb_array_length _null_ _null_ _null_ ));
+DESCR("length of jsonb array");
+DATA(insert OID = 3201 (  jsonb_object_keys			PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_object_keys _null_ _null_ _null_ ));
+DESCR("get jsonb object keys");
+DATA(insert OID = 3208 (  jsonb_each				   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,3802}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3202 (  jsonb_each_text		   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,25}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each_text _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3209 (  jsonb_populate_record	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_record _null_ _null_ _null_ ));
+DESCR("get record fields from a jsonb object");
+DATA(insert OID = 3203 (  jsonb_populate_recordset  PGNSP PGUID 12 1 100 0 0 f f f f f t s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_recordset _null_ _null_ _null_ ));
+DESCR("get set of records with fields from a jsonb array of objects");
+DATA(insert OID = 3210 (  jsonb_typeof              PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_typeof _null_ _null_ _null_ ));
+DESCR("get the type of a jsonb value");
+
+
 /* txid */
 DATA(insert OID = 2939 (  txid_snapshot_in			PGNSP PGUID 12 1  0 0 0 f f f f t f i 1 0 2970 "2275" _null_ _null_ _null_ _null_ txid_snapshot_in _null_ _null_ _null_ ));
 DESCR("I/O");
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 3fc20c6..7fb8999 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -600,6 +600,12 @@ DATA(insert OID = 3645 ( _tsquery		PGNSP PGUID -1 f b A f t \054 0 3615 0 array_
 DATA(insert OID = 3735 ( _regconfig		PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 
+/* jsonb */
+DATA(insert OID = 3802 ( jsonb			PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DESCR("Binary JSON");
+#define JSONBOID 3802
+DATA(insert OID = 3807 ( _jsonb			PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 DATA(insert OID = 2970 ( txid_snapshot	PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 DESCR("txid snapshot");
 DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 0233f4c..d399e83 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -52,4 +52,6 @@ extern void EventTriggerEndCompleteQuery(void);
 extern bool trackDroppedObjectsNeeded(void);
 extern void EventTriggerSQLDropAddObject(ObjectAddress *object);
 
+extern void PreCommitTriggersFire(void);
+
 #endif   /* EVENT_TRIGGER_H */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 9982e59..3610fc8 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -293,6 +293,15 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
 		PG_RETURN_DATUM(_result); \
 	} while (0)
 
+#define SRF_RETURN_NEXT_NULL(_funcctx) \
+	do { \
+		ReturnSetInfo	   *rsi; \
+		(_funcctx)->call_cntr++; \
+		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+		rsi->isDone = ExprMultipleResult; \
+		PG_RETURN_NULL(); \
+	} while (0)
+
 #define  SRF_RETURN_DONE(_funcctx) \
 	do { \
 		ReturnSetInfo	   *rsi; \
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index c4c284f..0f9ae89 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -20,7 +20,8 @@ typedef enum
 {
 	EVT_DDLCommandStart,
 	EVT_DDLCommandEnd,
-	EVT_SQLDrop
+	EVT_SQLDrop,
+	EVT_Commit
 } EventTriggerEvent;
 
 typedef struct
diff --git a/src/include/utils/json.h b/src/include/utils/json.h
index 25bfafb..c2fc7ee 100644
--- a/src/include/utils/json.h
+++ b/src/include/utils/json.h
@@ -50,4 +50,18 @@ extern Datum json_array_elements(PG_FUNCTION_ARGS);
 extern Datum json_populate_record(PG_FUNCTION_ARGS);
 extern Datum json_populate_recordset(PG_FUNCTION_ARGS);
 
+extern Datum jsonb_object_field(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_field_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_keys(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_length(PG_FUNCTION_ARGS);
+extern Datum jsonb_each(PG_FUNCTION_ARGS);
+extern Datum jsonb_each_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_elements(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_record(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_recordset(PG_FUNCTION_ARGS);
+
 #endif   /* JSON_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
new file mode 100644
index 0000000..0a417c5
--- /dev/null
+++ b/src/include/utils/jsonb.h
@@ -0,0 +1,231 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.h
+ *    Declarations for JSONB data type support.
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/include/utils/jsonb.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef __JSONB_H__
+#define __JSONB_H__
+
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "utils/array.h"
+#include "utils/numeric.h"
+
+/*
+ * JEntry: there is one of these for each key _and_ value in an jsonb
+ *
+ * the position offset points to the _end_ so that we can get the length
+ * by subtraction from the previous entry.	the ISFIRST flag lets us tell
+ * whether there is a previous entry.
+ */
+typedef struct
+{
+	uint32		entry;
+} JEntry;
+
+#define JENTRY_ISFIRST		0x80000000
+#define JENTRY_ISSTRING 	(0x00000000) /* keep binary compatibility */
+#define JENTRY_ISNUMERIC	(0x10000000)
+#define JENTRY_ISNEST		(0x20000000)
+#define JENTRY_ISNULL		(0x40000000) /* keep binary compatibility */
+#define JENTRY_ISBOOL		(0x10000000 | 0x20000000)
+#define JENTRY_ISFALSE		JENTRY_ISBOOL
+#define JENTRY_ISTRUE		(0x10000000 | 0x20000000 | 0x40000000)
+
+/* JENTRY_ISOBJECT, JENTRY_ISARRAY and JENTRY_ISCALAR is only used in send/recv */
+#define JENTRY_ISOBJECT		(0x20000000)
+#define JENTRY_ISARRAY		(0x20000000 | 0x40000000)
+#define JENTRY_ISCALAR		(0x10000000 | 0x40000000)
+
+#define JENTRY_POSMASK 	0x0FFFFFFF
+#define JENTRY_TYPEMASK	(~(JENTRY_POSMASK | JENTRY_ISFIRST))
+
+/* note possible multiple evaluations, also access to prior array element */
+#define JBE_ISFIRST(he_) 		(((he_).entry & JENTRY_ISFIRST) != 0)
+#define JBE_ISSTRING(he_)		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
+#define JBE_ISNUMERIC(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
+#define JBE_ISNEST(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNEST)
+#define JBE_ISNULL(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNULL)
+#define JBE_ISBOOL(he_) 		(((he_).entry & JENTRY_TYPEMASK & JENTRY_ISBOOL) == JENTRY_ISBOOL)
+#define JBE_ISBOOL_TRUE(he_) 	(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISTRUE)
+#define JBE_ISBOOL_FALSE(he_) 	(JBE_ISBOOL(he_) && !JBE_ISBOOL_TRUE(he_))
+
+#define JBE_ENDPOS(he_) ((he_).entry & JENTRY_POSMASK)
+#define JBE_OFF(he_) (JBE_ISFIRST(he_) ? 0 : JBE_ENDPOS((&(he_))[-1]))
+#define JBE_LEN(he_) (JBE_ISFIRST(he_)	\
+					  ? JBE_ENDPOS(he_) \
+					  : JBE_ENDPOS(he_) - JBE_ENDPOS((&(he_))[-1]))
+
+/*
+ * determined by the size of "endpos" (ie JENTRY_POSMASK)
+ */
+#define JSONB_MAX_STRING_LEN 		JENTRY_POSMASK
+
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	/* header of hash or array jsonb type */
+	/* array of JEntry follows */
+} Jsonb;
+
+/*
+ * it's not possible to get more than 2^28 items into an jsonb.
+ */
+#define JB_FLAG_UNUSED 			0x80000000
+#define JB_FLAG_ARRAY			0x40000000
+#define JB_FLAG_OBJECT			0x20000000
+#define JB_FLAG_SCALAR			0x10000000
+
+#define JB_COUNT_MASK			0x0FFFFFFF
+
+#define JB_ISEMPTY(jbp_)		(VARSIZE(jbp_) <= VARHDRSZ)
+#define JB_ROOT_COUNT(jbp_) 	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_COUNT_MASK))
+#define JB_ROOT_IS_OBJECT(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_OBJECT))
+#define JB_ROOT_IS_ARRAY(jbp_) 	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_ARRAY))
+#define JB_ROOT_IS_SCALAR(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_SCALAR))
+
+#define DatumGetJsonb(d) 	((Jsonb*) PG_DETOAST_DATUM(d))
+#define JsonbGetDatum(p)	PointerGetDatum(p)
+
+#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x)
+
+typedef struct JsonbPair JsonbPair;
+typedef struct JsonbValue JsonbValue;
+
+struct JsonbValue {
+	enum {
+		jbvNull,
+		jbvString,
+		jbvNumeric,
+		jbvBool,
+		jbvArray,
+		jbvHash,
+		jbvBinary  /* binary form of jbvArray/jbvHash */
+	} type;
+
+	uint32		size; /* estimation size of node (including subnodes) */
+
+	union {
+		Numeric			numeric;
+		bool			boolean;
+		struct {
+			uint32		len;
+			char 		*val; /* could be not null-terminated */
+		} string;
+
+		struct {
+			int			nelems;
+			JsonbValue	*elems;
+			bool		scalar; /* scalar actually shares representation with array */
+		} array;
+
+		struct {
+			int			npairs;
+			JsonbPair 	*pairs;
+		} hash;
+
+		struct {
+			uint32		len;
+			char		*data;
+		} binary;
+	};
+
+}; 
+
+struct JsonbPair {
+	JsonbValue	key;
+	JsonbValue	value;
+	uint32		order; /* to keep order of pairs with equal key */ 
+}; 
+
+/*
+ * jsonb support functios
+ */
+
+#define WJB_KEY         	(0x001)
+#define WJB_VALUE       	(0x002)
+#define WJB_ELEM       		(0x004)
+#define WJB_BEGIN_ARRAY 	(0x008)
+#define WJB_END_ARRAY   	(0x010)
+#define WJB_BEGIN_OBJECT    (0x020)
+#define WJB_END_OBJECT      (0x040)
+
+typedef void (*walk_jsonb_cb)(void* /*arg*/, JsonbValue* /* value */, 
+											uint32 /* flags */, uint32 /* level */);
+extern void walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg);
+
+extern int compareJsonbStringValue(const void *a, const void *b, void *arg);
+extern int compareJsonbPair(const void *a, const void *b, void *arg);
+
+extern int compareJsonbBinaryValue(char *a, char *b);
+extern int compareJsonbValue(JsonbValue *a, JsonbValue *b);
+
+extern JsonbValue* findUncompressedJsonbValueByValue(char *buffer, uint32 flags, 
+												uint32 *lowbound, JsonbValue* key);
+extern JsonbValue* findUncompressedJsonbValue(char *buffer, uint32 flags, 
+												uint32 *lowbound, char *key, uint32 keylen);
+
+extern JsonbValue* getJsonbValue(char *buffer, uint32 flags, int32 i);
+
+extern bool JsonbStringIsNumber(char *string, int len);
+
+typedef struct ToJsonbState
+{
+	JsonbValue             v;
+	uint32                  size;
+	struct ToJsonbState    *next;
+} ToJsonbState;
+
+extern JsonbValue* pushJsonbValue(ToJsonbState **state, int r /* WJB_* */, JsonbValue *v);
+
+extern void uniqueJsonbValue(JsonbValue *v);
+
+extern uint32 compressJsonb(JsonbValue *v, char *buffer);
+
+typedef struct JsonbIterator
+{
+	uint32					type;
+	uint32					nelems;
+	JEntry					*array;
+	bool					isScalar;
+	char					*data;
+	char					*buffer; /* unparsed buffer */
+
+	int						i;
+
+	/*
+	 * enum members should be freely OR'ed with JB_FLAG_ARRAY/JB_FLAG_JSONB 
+	 * with possiblity of decoding. See optimization in JsonbIteratorGet()
+	 */
+	enum {
+		jbi_start 	= 0x00,
+		jbi_key		= 0x01,
+		jbi_value	= 0x02,
+		jbi_elem	= 0x04
+	} state;
+
+	struct JsonbIterator	*next;
+} JsonbIterator;
+
+extern 	JsonbIterator*	JsonbIteratorInit(char *buffer);
+extern	int /* WJB_* */	JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested);
+
+extern Datum jsonb_in(PG_FUNCTION_ARGS);
+extern Datum jsonb_out(PG_FUNCTION_ARGS);
+extern Datum jsonb_recv(PG_FUNCTION_ARGS);
+extern Datum jsonb_send(PG_FUNCTION_ARGS);
+
+extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
+
+extern char *JsonbToCString(StringInfo out, char *in, int estimated_len);
+
+#endif   /* __JSONB_H__ */
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
new file mode 100644
index 0000000..78590ba
--- /dev/null
+++ b/src/test/regress/expected/jsonb.out
@@ -0,0 +1,836 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+SELECT '"\uaBcD"'::jsonb;		-- OK, uppercase and lower case both OK
+ jsonb 
+-------
+ "ꯍ"
+(1 row)
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot extract element from a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot extract field from a non-object
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot extract element from a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  cannot extract array element from a non-array
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  Cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+-- handling of unicode surrogate pairs
+select jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a' as correct_in_utf8;
+ correct_in_utf8 
+-----------------
+ "😄🐶"
+(1 row)
+
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+   correct_in_utf8    
+----------------------
+ the Copyright © sign
+(1 row)
+
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
new file mode 100644
index 0000000..0166883
--- /dev/null
+++ b/src/test/regress/expected/jsonb_1.out
@@ -0,0 +1,836 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+SELECT '"\uaBcD"'::jsonb;		-- OK, uppercase and lower case both OK
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\uaBcD"'::jsonb;
+               ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: ...
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot extract element from a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot extract field from a non-object
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot extract element from a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  cannot extract array element from a non-array
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  Cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+-- handling of unicode surrogate pairs
+select jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a' as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a' a...
+                     ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a'...
+                     ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 5758b07..51238be 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -98,8 +98,7 @@ test: event_trigger
 # ----------
 # Another group of parallel tests
 # ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json indirect_toast
-
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast
 # ----------
 # Another group of parallel tests
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 78348f5..e414ec1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -121,6 +121,7 @@ test: xmlmap
 test: functional_deps
 test: advisory_lock
 test: json
+test: jsonb
 test: indirect_toast
 test: plancache
 test: limit
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
new file mode 100644
index 0000000..ee1f508
--- /dev/null
+++ b/src/test/regress/sql/jsonb.sql
@@ -0,0 +1,259 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+SELECT '"abc"'::jsonb;			-- OK
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+SELECT '"\uaBcD"'::jsonb;		-- OK, uppercase and lower case both OK
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+SELECT '0'::jsonb;				-- OK
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+SELECT '0.1'::jsonb;				-- OK
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+SELECT '1e100'::jsonb;			-- OK
+SELECT '1.3e100'::jsonb;			-- OK
+SELECT '1f2'::jsonb;				-- ERROR
+SELECT '0.x1'::jsonb;			-- ERROR
+SELECT '1.3ex100'::jsonb;		-- ERROR
+
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+SELECT '[1,2]'::jsonb;			-- OK
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+SELECT '{"abc":1}'::jsonb;		-- OK
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+SELECT 'false'::jsonb;			-- OK
+SELECT 'null'::jsonb;			-- OK
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+SELECT ''::jsonb;				-- ERROR, no value
+SELECT '    '::jsonb;			-- ERROR, no value
+
+-- jsonb extraction functions
+
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+
+-- nulls
+
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+
+
+-- array length
+
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+
+SELECT jsonb_array_length('[]');
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+
+SELECT jsonb_array_length('4');
+
+-- each
+
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+-- extract_path, extract_path_as_text
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+
+-- extract_path nulls
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+
+-- extract_path operators
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+
+-- array_elements
+
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+
+-- populate_recordset
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+
+-- using the default use_json_as_text argument
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+
+
+-- handling of unicode surrogate pairs
+
+select jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a' as correct_in_utf8;
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+
+--handling of simple unicode escapes
+
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
#30Oleg Bartunov
obartunov@gmail.com
In reply to: Andrew Dunstan (#29)
Re: nested hstore patch

I moved patch to the January commitfest
(https://commitfest.postgresql.org/action/patch_view?id=1289) .

Oleg

PS.

Kudos to Teodor and his mobile phone, which he used to synchronize
branches on github.

On Fri, Jan 10, 2014 at 2:08 AM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/09/2014 02:11 PM, Josh Berkus wrote:

On 01/09/2014 06:12 AM, Andrew Dunstan wrote:

Oleg,

Please merge in the jsonb work and resubmit. See
<https://github.com/feodor/postgres/commits/jsonb_and_hstore&gt; I note that
this repo does not apparently contain any of your latest changes.

I'll go further and say that if the Hstore2 patch doesn't support JSONB
for 9.4, we should postpone it to 9.5. We really don't want to get into
a situation where we need an Hstore3 because we accepted an Hstore2
which needs to be rev'd for JSON.

Especially since there's no good reason for the JSON changes not to be
merged already.

After some work by Oleg, for which I'm grateful, and a little more by me,
here is a combined patch for the jsonb and nested hstore work.

Outstanding issues with the jsonb stuff:

* I have replicated all the json processing functions for jsonb
(although not the json generating functions, such as to_json). Most
of these currently work by turning the jsonb back into json and then
processing as before. I am sorting out some technical issues and
hope to have all of these rewritten to use the native jsonb API in a
few days time.
* We still need to document jsonb. That too I hope will be done quite
shortly.
* The jsonb regression test currently contains U+ABCD - I guess we'd
better use some hex encoding or whatever for that - unlike json, the
jsonb de-serializer dissolves unicode escapes.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Robert Haas
robertmhaas@gmail.com
In reply to: Andrew Dunstan (#29)
Re: nested hstore patch

On Thu, Jan 9, 2014 at 5:08 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

* I have replicated all the json processing functions for jsonb
(although not the json generating functions, such as to_json). Most
of these currently work by turning the jsonb back into json and then
processing as before. I am sorting out some technical issues and
hope to have all of these rewritten to use the native jsonb API in a
few days time.
* We still need to document jsonb. That too I hope will be done quite
shortly.
* The jsonb regression test currently contains U+ABCD - I guess we'd
better use some hex encoding or whatever for that - unlike json, the
jsonb de-serializer dissolves unicode escapes.

How does that work if the server encoding isn't UTF-8?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#32Andrew Dunstan
andrew@dunslane.net
In reply to: Robert Haas (#31)
Re: nested hstore patch

On 01/10/2014 01:29 PM, Robert Haas wrote:

On Thu, Jan 9, 2014 at 5:08 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

* The jsonb regression test currently contains U+ABCD - I guess we'd
better use some hex encoding or whatever for that - unlike json, the
jsonb de-serializer dissolves unicode escapes.

How does that work if the server encoding isn't UTF-8?

There is a jsonb_1.out file for the non-utf8 case, just as there is a
json_1.out for the same case. Unicode escapes for non-ascii characters
are forbidden in jsonb as they are in json, if the encoding isn't utf8.

FYI, we are actually using the json lexing and parsing mechanism, so
that these types will accept exactly the same inputs. However, since
we're not storing json text in jsonb, but instead the decomposed
elements, the unicode escapes are resolved in the stored values.

I already have a fix for the point above (see
<https://github.com/feodor/postgres/commit/7d5b8f12747b4a75e8b32914340d07617f1af302&gt;)
and it will be included in the next version of the patch.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#33Peter Eisentraut
peter_e@gmx.net
In reply to: Andrew Dunstan (#29)
Re: nested hstore patch

The documentation doesn't build.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34Erik Rijkers
er@xs4all.nl
In reply to: Peter Eisentraut (#33)
Re: nested hstore patch

On Sat, January 11, 2014 20:30, Peter Eisentraut wrote:

The documentation doesn't build.

corrective patch is here:

/messages/by-id/37b9f104d5a838eec9b75f3668517aa5.squirrel@webmail.xs4all.nl

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Andrew Dunstan
andrew@dunslane.net
In reply to: Erik Rijkers (#34)
Re: nested hstore patch

On 01/11/2014 03:03 PM, Erik Rijkers wrote:

On Sat, January 11, 2014 20:30, Peter Eisentraut wrote:

The documentation doesn't build.

corrective patch is here:

/messages/by-id/37b9f104d5a838eec9b75f3668517aa5.squirrel@webmail.xs4all.nl

It's been committed at
<https://github.com/feodor/postgres/commit/a21a4be55a5b12c4bd89b6ab2f77cf32e319de31&gt;.
It will be in the next version of the patch posted.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36David E. Wheeler
david@justatheory.com
In reply to: Andrew Dunstan (#35)
Re: nested hstore patch

On Jan 11, 2014, at 1:47 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

It's been committed at <https://github.com/feodor/postgres/commit/a21a4be55a5b12c4bd89b6ab2f77cf32e319de31&gt;. It will be in the next version of the patch posted.

Bah! Sorry about that. Habit from decades of typing HTML.

David

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Erik Rijkers
er@xs4all.nl
In reply to: Andrew Dunstan (#35)
1 attachment(s)
Re: nested hstore patch

On Sat, January 11, 2014 22:47, Andrew Dunstan wrote:

On 01/11/2014 03:03 PM, Erik Rijkers wrote:

On Sat, January 11, 2014 20:30, Peter Eisentraut wrote:

The documentation doesn't build.

corrective patch is here:

/messages/by-id/37b9f104d5a838eec9b75f3668517aa5.squirrel@webmail.xs4all.nl

It will be in the next version of the patch posted.

Attached is another handful of doc-fixes...

Attachments:

hstore.sgml.20140112.difftext/x-patch; name=hstore.sgml.20140112.diffDownload
--- doc/src/sgml/hstore.sgml.orig	2014-01-12 15:37:59.292863864 +0100
+++ doc/src/sgml/hstore.sgml	2014-01-13 00:17:51.454592023 +0100
@@ -55,11 +55,11 @@
    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 continaining whitespace,
+   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 represnted as <literal>true</>, <literal>t</>,
-   <literal>false</>, or <literal>f</>. Use quotation marks to represent thes
+   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:
@@ -87,9 +87,9 @@
   </para>
 
   <para>
-   Hashes includes zero or more
+   Hashes include zero or more
    <replaceable>key</> <literal>=&gt;</> <replaceable>value</> pairs separated
-   by commas, optionally brackted by curly braces. Keys must be strings and
+   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:
 
@@ -291,7 +291,7 @@
 
      <row>
       <entry><type>hstore</> <literal>?&gt;</> <type>integer</></entry>
-      <entry><type>bolean</></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>f</literal></entry>
@@ -316,7 +316,7 @@
      <row>
       <entry><type>hstore</> <literal>#?&gt;</> <type>text[]</></entry>
       <entry><type>boolean</></entry>
-      <entry>get boolean value for key path (<literal>NULL</> if not booelan or not present)</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>t</literal></entry>
      </row>
@@ -905,7 +905,7 @@
 
      <row>
       <entry><literal>pretty_print</></entry>
-      <entry>Adds add newlines between values and indents nested hashes and arrays.</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>
@@ -938,7 +938,7 @@
 
      <row>
       <entry><literal>json</></entry>
-      <entry>Retuns the value as a JSON string</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>
@@ -1007,7 +1007,7 @@
 
   <para>
    But <literal>populate_record()</> supports more complicated records and nested
-   <type>hstore</a> values, as well. It makes an effort to convert
+   <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>
#38Erik Rijkers
er@xs4all.nl
In reply to: Erik Rijkers (#37)
2 attachment(s)
Re: nested hstore patch

On Mon, January 13, 2014 00:24, Erik Rijkers wrote:

On Sat, January 11, 2014 22:47, Andrew Dunstan wrote:

On 01/11/2014 03:03 PM, Erik Rijkers wrote:

On Sat, January 11, 2014 20:30, Peter Eisentraut wrote:

The documentation doesn't build.

corrective patch is here:

/messages/by-id/37b9f104d5a838eec9b75f3668517aa5.squirrel@webmail.xs4all.nl

It will be in the next version of the patch posted.

Attached is another handful of doc-fixes...

There are errors in the example expressions in "Table F-6. hstore Operators".

Attached is a cumulative doc-patch (which includes the changes I sent earlier) which fixes these.

I also attach an test perl program that shows the (small) differences in output between what's in that doc table and what
one actually gets. (I found these too insignificant to change but perhaps you have a different opinion.)

thanks,

Erik Rijkers

Attachments:

hstore.sgml.20140113.difftext/x-patch; name=hstore.sgml.20140113.diffDownload
--- doc/src/sgml/hstore.sgml.orig	2014-01-12 15:37:59.292863864 +0100
+++ doc/src/sgml/hstore.sgml	2014-01-13 07:40:31.029280550 +0100
@@ -55,11 +55,11 @@
    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 continaining whitespace,
+   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 represnted as <literal>true</>, <literal>t</>,
-   <literal>false</>, or <literal>f</>. Use quotation marks to represent thes
+   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:
@@ -87,9 +87,9 @@
   </para>
 
   <para>
-   Hashes includes zero or more
+   Hashes include zero or more
    <replaceable>key</> <literal>=&gt;</> <replaceable>value</> pairs separated
-   by commas, optionally brackted by curly braces. Keys must be strings and
+   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:
 
@@ -291,7 +291,7 @@
 
      <row>
       <entry><type>hstore</> <literal>?&gt;</> <type>integer</></entry>
-      <entry><type>bolean</></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>f</literal></entry>
@@ -301,7 +301,7 @@
       <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>'foo =&gt; {bar =&gt; yellow}'::hstore #&gt; '{foo,bar}'</literal></entry>
       <entry><literal>yellow</literal></entry>
      </row>
 
@@ -309,15 +309,15 @@
       <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>'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 booelan or not present)</entry>
-      <entry><literal>'foo =&gt; {bar =&gt; true}'::hstore #?&gt; '[foo,bar]'</literal></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>t</literal></entry>
      </row>
 
@@ -333,7 +333,7 @@
       <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>'[1, 2, {foo=>hi}]'::hstore %> 2</literal></entry>
       <entry><literal>"foo"=&gt;"hi"</literal></entry>
      </row>
 
@@ -341,7 +341,7 @@
       <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>'a =&gt; 1, b =&gt; {c =&gt; [44,44]}'::hstore #%&gt; '{b,c}'</literal></entry>
       <entry><literal>[44, 44]</literal></entry>
      </row>
 
@@ -373,7 +373,7 @@
       <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>'[a,b,c]'::hstore ? 2</literal></entry>
       <entry><literal>t</literal></entry>
      </row>
 
@@ -381,7 +381,7 @@
       <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>'[1, 2, {foo=&gt;hi}]'::hstore #? '{2,foo}'</literal></entry>
       <entry><literal>t</literal></entry>
      </row>
 
@@ -429,7 +429,7 @@
       <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, 3, 4, 6, 8]'::hstore - 1</literal></entry>
       <entry><literal>[2, 4, 6, 8]</literal></entry>
      </row>
 
@@ -905,7 +905,7 @@
 
      <row>
       <entry><literal>pretty_print</></entry>
-      <entry>Adds add newlines between values and indents nested hashes and arrays.</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>
@@ -938,7 +938,7 @@
 
      <row>
       <entry><literal>json</></entry>
-      <entry>Retuns the value as a JSON string</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>
@@ -1007,7 +1007,7 @@
 
   <para>
    But <literal>populate_record()</> supports more complicated records and nested
-   <type>hstore</a> values, as well. It makes an effort to convert
+   <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>
table_hstore_operators.plapplication/x-perl; name=table_hstore_operators.plDownload
#39Oleg Bartunov
obartunov@gmail.com
In reply to: Erik Rijkers (#38)
Re: nested hstore patch

Thank you, Erik !

Oleg

On Mon, Jan 13, 2014 at 12:25 PM, Erik Rijkers <er@xs4all.nl> wrote:

On Mon, January 13, 2014 00:24, Erik Rijkers wrote:

On Sat, January 11, 2014 22:47, Andrew Dunstan wrote:

On 01/11/2014 03:03 PM, Erik Rijkers wrote:

On Sat, January 11, 2014 20:30, Peter Eisentraut wrote:

The documentation doesn't build.

corrective patch is here:

/messages/by-id/37b9f104d5a838eec9b75f3668517aa5.squirrel@webmail.xs4all.nl

It will be in the next version of the patch posted.

Attached is another handful of doc-fixes...

There are errors in the example expressions in "Table F-6. hstore Operators".

Attached is a cumulative doc-patch (which includes the changes I sent earlier) which fixes these.

I also attach an test perl program that shows the (small) differences in output between what's in that doc table and what
one actually gets. (I found these too insignificant to change but perhaps you have a different opinion.)

thanks,

Erik Rijkers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40Andrew Dunstan
andrew@dunslane.net
In reply to: Erik Rijkers (#38)
1 attachment(s)
Re: nested hstore patch

On 01/13/2014 03:25 AM, Erik Rijkers wrote:

There are errors in the example expressions in "Table F-6. hstore Operators".

Attached is a cumulative doc-patch (which includes the changes I sent earlier) which fixes these.

I also attach an test perl program that shows the (small) differences in output between what's in that doc table and what
one actually gets. (I found these too insignificant to change but perhaps you have a different opinion.)

A new version of the patch is attached. It includes all of Erik's docs
fixes and a small fix by Alexander Korotkov for hstore hash ops.

cheers

andrew

Attachments:

nested_hstore_and_jsonb-2.patchtext/x-patch; name=nested_hstore_and_jsonb-2.patchDownload
diff --git a/contrib/hstore/.gitignore b/contrib/hstore/.gitignore
index 5dcb3ff..b84aac6 100644
--- a/contrib/hstore/.gitignore
+++ b/contrib/hstore/.gitignore
@@ -2,3 +2,6 @@
 /log/
 /results/
 /tmp_check/
+/hstore_gram.c
+/hstore_gram.h
+/hstore_scan.c
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 43b7e5f..fb9421f 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -2,13 +2,16 @@
 
 MODULE_big = hstore
 OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
-	crc32.o
+		hstore_gram.o
 
 EXTENSION = hstore
-DATA = hstore--1.2.sql hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
-	hstore--unpackaged--1.0.sql
+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
 
-REGRESS = hstore
+REGRESS = hstore nested types
+
+EXTRA_CLEAN = y.tab.c y.tab.h \
+				hstore_gram.c hstore_scan.c hstore_gram.h
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
@@ -20,3 +23,13 @@ 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
deleted file mode 100644
index c82fc66..0000000
--- a/contrib/hstore/crc32.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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 <srg@quick.com>
- */
-
-#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
deleted file mode 100644
index f5bfd82..0000000
--- a/contrib/hstore/crc32.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * 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/expected/hstore.out b/contrib/hstore/expected/hstore.out
index 2114143..bf22abc 100644
--- a/contrib/hstore/expected/hstore.out
+++ b/contrib/hstore/expected/hstore.out
@@ -199,6 +199,24 @@ select 'aa=>"NuLl"'::hstore;
  "aa"=>"NuLl"
 (1 row)
 
+select 'aa=>nul'::hstore;
+   hstore    
+-------------
+ "aa"=>"nul"
+(1 row)
+
+select 'aa=>NuL'::hstore;
+   hstore    
+-------------
+ "aa"=>"NuL"
+(1 row)
+
+select 'aa=>"NuL"'::hstore;
+   hstore    
+-------------
+ "aa"=>"NuL"
+(1 row)
+
 select e'\\=a=>q=w'::hstore;
    hstore    
 -------------
@@ -432,63 +450,63 @@ select hstore 'a=>NULL, b=>qq' ?& '{}'::text[];
 
 -- delete
 select delete('a=>1 , b=>2, c=>3'::hstore, 'a');
-       delete       
---------------------
- "b"=>"2", "c"=>"3"
+     delete     
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>null , b=>2, c=>3'::hstore, 'a');
-       delete       
---------------------
- "b"=>"2", "c"=>"3"
+     delete     
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'b');
-       delete       
---------------------
- "a"=>"1", "c"=>"3"
+     delete     
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'c');
-       delete       
---------------------
- "a"=>"1", "b"=>"2"
+     delete     
+----------------
+ "a"=>1, "b"=>2
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'd');
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'a'::text;
-      ?column?      
---------------------
- "b"=>"2", "c"=>"3"
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>null , b=>2, c=>3'::hstore - 'a'::text;
-      ?column?      
---------------------
- "b"=>"2", "c"=>"3"
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'b'::text;
-      ?column?      
---------------------
- "a"=>"1", "c"=>"3"
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'c'::text;
-      ?column?      
---------------------
- "a"=>"1", "b"=>"2"
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'd'::text;
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text)
@@ -500,21 +518,21 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text)
 
 -- delete (array)
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','e']);
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','b']);
-       delete       
---------------------
- "a"=>"1", "c"=>"3"
+     delete     
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['a','c']);
-  delete  
-----------
- "b"=>"2"
+ delete 
+--------
+ "b"=>2
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]);
@@ -524,27 +542,27 @@ select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]);
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, '{}'::text[]);
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','e'];
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','b'];
-      ?column?      
---------------------
- "a"=>"1", "c"=>"3"
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c'];
  ?column? 
 ----------
- "b"=>"2"
+ "b"=>2
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']];
@@ -554,9 +572,9 @@ select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']];
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - '{}'::text[];
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c'])
@@ -575,15 +593,15 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - '{}'::text[])
 
 -- delete (hstore)
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>4, b=>2'::hstore);
-       delete        
----------------------
- "c"=>"3", "aa"=>"1"
+     delete      
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>NULL, c=>3'::hstore);
-       delete        
----------------------
- "b"=>"2", "aa"=>"1"
+     delete      
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore);
@@ -593,27 +611,27 @@ select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore);
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'b=>2'::hstore);
-       delete        
----------------------
- "c"=>"3", "aa"=>"1"
+     delete      
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, ''::hstore);
-            delete             
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+         delete          
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>4, b=>2'::hstore;
-      ?column?       
----------------------
- "c"=>"3", "aa"=>"1"
+    ?column?     
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>NULL, c=>3'::hstore;
-      ?column?       
----------------------
- "b"=>"2", "aa"=>"1"
+    ?column?     
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore;
@@ -623,15 +641,15 @@ select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore;
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore;
-      ?column?       
----------------------
- "c"=>"3", "aa"=>"1"
+    ?column?     
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - ''::hstore;
-           ?column?            
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+        ?column?         
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore)
@@ -650,33 +668,33 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ''::hstore)
 
 -- ||
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f';
-                 ?column?                  
--------------------------------------------
- "b"=>"g", "aa"=>"1", "cq"=>"l", "fg"=>"f"
+               ?column?                
+---------------------------------------
+ "b"=>"g", "aa"=>1, "cq"=>"l", "fg"=>f
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'aq=>l';
-                 ?column?                  
--------------------------------------------
- "b"=>"2", "aa"=>"1", "aq"=>"l", "cq"=>"3"
+              ?column?               
+-------------------------------------
+ "b"=>2, "aa"=>1, "aq"=>"l", "cq"=>3
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'aa=>l';
-            ?column?            
---------------------------------
- "b"=>"2", "aa"=>"l", "cq"=>"3"
+          ?column?          
+----------------------------
+ "b"=>2, "aa"=>"l", "cq"=>3
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || '';
-            ?column?            
---------------------------------
- "b"=>"2", "aa"=>"1", "cq"=>"3"
+         ?column?         
+--------------------------
+ "b"=>2, "aa"=>1, "cq"=>3
 (1 row)
 
 select ''::hstore || 'cq=>l, b=>g, fg=>f';
-            ?column?            
---------------------------------
- "b"=>"g", "cq"=>"l", "fg"=>"f"
+           ?column?           
+------------------------------
+ "b"=>"g", "cq"=>"l", "fg"=>f
 (1 row)
 
 select pg_column_size(''::hstore || ''::hstore) = pg_column_size(''::hstore);
@@ -759,21 +777,21 @@ select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']);
-       slice        
---------------------
- "b"=>"2", "c"=>"3"
+     slice      
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['aa','b']);
-        slice        
----------------------
- "b"=>"2", "aa"=>"1"
+      slice      
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b','aa']);
-             slice             
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+          slice          
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select pg_column_size(slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']))
@@ -907,9 +925,9 @@ select pg_column_size(hstore(ARRAY['a','b','asd'], ARRAY['g','h','i']))
 
 -- records
 select hstore(v) from (values (1, 'foo', 1.2, 3::float8)) v(a,b,c,d);
-                   hstore                   
---------------------------------------------
- "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3"
+                  hstore                  
+------------------------------------------
+ "a"=>"1", "b"=>"foo", "c"=>1.2, "d"=>"3"
 (1 row)
 
 create domain hstestdom1 as integer not null default 0;
@@ -918,9 +936,9 @@ create table testhstore1 (a integer, b text, c numeric, d float8, e hstestdom1);
 insert into testhstore0 values (1, 'foo', 1.2, 3::float8);
 insert into testhstore1 values (1, 'foo', 1.2, 3::float8);
 select hstore(v) from testhstore1 v;
-                        hstore                        
-------------------------------------------------------
- "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3", "e"=>"0"
+                       hstore                       
+----------------------------------------------------
+ "a"=>"1", "b"=>"foo", "c"=>1.2, "d"=>"3", "e"=>"0"
 (1 row)
 
 select hstore(null::testhstore0);
@@ -936,7 +954,7 @@ select hstore(null::testhstore1);
 (1 row)
 
 select pg_column_size(hstore(v))
-         = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore)
+         = pg_column_size('a=>"1", b=>"foo", c=>1.2, d=>"3", e=>"0"'::hstore)
   from testhstore1 v;
  ?column? 
 ----------
@@ -1336,6 +1354,7 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
     42
 (1 row)
 
+RESET enable_seqscan;
 drop index hidx;
 create index hidx on testhstore using gin (h);
 set enable_seqscan=off;
@@ -1375,6 +1394,7 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
     42
 (1 row)
 
+RESET enable_seqscan;
 select count(*) from (select (each(h)).key from testhstore) as wow ;
  count 
 -------
@@ -1437,6 +1457,8 @@ select distinct * from (values (hstore '' || ''),('')) v(h);
 (1 row)
 
 set enable_sort = true;
+RESET enable_hashagg;
+RESET enable_sort;
 -- btree
 drop index hidx;
 create index hidx on testhstore using btree (h);
@@ -1444,7 +1466,7 @@ set enable_seqscan=off;
 select count(*) from testhstore where h #># 'p=>1';
  count 
 -------
-   125
+   884
 (1 row)
 
 select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
@@ -1453,39 +1475,63 @@ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexe
      1
 (1 row)
 
+--gin hash
+drop index hidx;
+create index hidx on testhstore using gin (h gin_hstore_hash_ops);
+set enable_seqscan=off;
+select count(*) from testhstore where h @> 'wait=>NULL';
+ count 
+-------
+     1
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC';
+ count 
+-------
+    15
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+ count 
+-------
+     2
+(1 row)
+
+RESET enable_seqscan;
+drop index hidx;
 -- json
 select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
-                                         hstore_to_json                                          
--------------------------------------------------------------------------------------------------
- {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+                                    hstore_to_json                                     
+---------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
-                                              json                                               
--------------------------------------------------------------------------------------------------
- {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+                                         json                                          
+---------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
-                                   hstore_to_json_loose                                   
-------------------------------------------------------------------------------------------
- {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}
+                                 hstore_to_json_loose                                  
+---------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 create table test_json_agg (f1 text, f2 hstore);
 insert into test_json_agg values ('rec1','"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'),
        ('rec2','"a key" =>2, b => f, c => "null", d=> -12345, e => 012345.6, f=> -1.234, g=> 0.345e-4');
 select json_agg(q) from test_json_agg q;
-                                                          json_agg                                                          
-----------------------------------------------------------------------------------------------------------------------------
- [{"f1":"rec1","f2":{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}},      +
-  {"f1":"rec2","f2":{"b": "f", "c": "null", "d": "-12345", "e": "012345.6", "f": "-1.234", "g": "0.345e-4", "a key": "2"}}]
+                                                       json_agg                                                        
+-----------------------------------------------------------------------------------------------------------------------
+ [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}},           +
+  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.0000345, "a key": 2}}]
 (1 row)
 
 select json_agg(q) from (select f1, hstore_to_json_loose(f2) as f2 from test_json_agg) q;
-                                                       json_agg                                                       
-----------------------------------------------------------------------------------------------------------------------
- [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}},       +
-  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.345e-4, "a key": 2}}]
+                                                       json_agg                                                        
+-----------------------------------------------------------------------------------------------------------------------
+ [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}},           +
+  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.0000345, "a key": 2}}]
 (1 row)
 
diff --git a/contrib/hstore/expected/nested.out b/contrib/hstore/expected/nested.out
new file mode 100644
index 0000000..a7786ce8
--- /dev/null
+++ b/contrib/hstore/expected/nested.out
@@ -0,0 +1,2249 @@
+SELECT 'ff => {a=>12, b=>16}'::hstore;
+          hstore          
+--------------------------
+ "ff"=>{"a"=>12, "b"=>16}
+(1 row)
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123'::hstore;
+               hstore                
+-------------------------------------
+ "ff"=>{"a"=>12, "b"=>16}, "qq"=>123
+(1 row)
+
+SELECT 'aa => {a,aaa}, qq=>{ a=>12, b=>16 , c=> { c1, c2}, d=>{d1=>d1, d2=>d2, d1=>d3} }'::hstore;
+                                             hstore                                             
+------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>12, "b"=>16, "c"=>["c1", "c2"], "d"=>{"d1"=>"d3", "d2"=>"d2"}}
+(1 row)
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+                                               hstore                                               
+----------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>"12", "b"=>"16", "c"=>["c1", "c2"], "d"=>{"d1"=>"d1", "d2"=>"d2"}}
+(1 row)
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+                                                        hstore                                                         
+-----------------------------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>"12", "b"=>"16", "c"=>["c1", "c2", ["c3"], {"c4"=>4}], "d"=>{"d1"=>"d1", "d2"=>"d2"}}
+(1 row)
+
+SELECT 'ff => {a,aaa}'::hstore;
+       hstore       
+--------------------
+ "ff"=>["a", "aaa"]
+(1 row)
+
+select 'null'::hstore;
+ hstore 
+--------
+ NULL
+(1 row)
+
+select '{null}'::hstore;
+ hstore 
+--------
+ [NULL]
+(1 row)
+
+select ''::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+select '{}'::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+--test optional outer braces
+SELECT	'a=>1'::hstore;
+ hstore 
+--------
+ "a"=>1
+(1 row)
+
+SELECT	'{a=>1}'::hstore;
+ hstore 
+--------
+ "a"=>1
+(1 row)
+
+SELECT	'{a,b}'::hstore;
+   hstore   
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT	'{a,{b}}'::hstore;
+    hstore    
+--------------
+ ["a", ["b"]]
+(1 row)
+
+SELECT	'{{a},b}'::hstore;
+    hstore    
+--------------
+ [["a"], "b"]
+(1 row)
+
+SELECT	'{a,{b},{c}}'::hstore;
+       hstore        
+---------------------
+ ["a", ["b"], ["c"]]
+(1 row)
+
+SELECT	'{{a},{b},c}'::hstore;
+       hstore        
+---------------------
+ [["a"], ["b"], "c"]
+(1 row)
+
+SELECT	'{{a},b,{c}}'::hstore;
+       hstore        
+---------------------
+ [["a"], "b", ["c"]]
+(1 row)
+
+SELECT	'{a,{b=>1}}'::hstore;
+     hstore      
+-----------------
+ ["a", {"b"=>1}]
+(1 row)
+
+SELECT	'{{a},{b=>1}}'::hstore;
+      hstore       
+-------------------
+ [["a"], {"b"=>1}]
+(1 row)
+
+SELECT	'a'::hstore;
+ hstore 
+--------
+ "a"
+(1 row)
+
+SELECT	'{a}'::hstore;
+ hstore 
+--------
+ ["a"]
+(1 row)
+
+SELECT	''::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+SELECT	'{}'::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+--nested json
+SELECT	hstore_to_json('a=>1');
+ hstore_to_json 
+----------------
+ {"a": 1}
+(1 row)
+
+SELECT	hstore_to_json('{a=>1}');
+ hstore_to_json 
+----------------
+ {"a": 1}
+(1 row)
+
+SELECT	hstore_to_json('{a,b}');
+ hstore_to_json 
+----------------
+ ["a", "b"]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b}}');
+ hstore_to_json 
+----------------
+ ["a", ["b"]]
+(1 row)
+
+SELECT	hstore_to_json('{{a},b}');
+ hstore_to_json 
+----------------
+ [["a"], "b"]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b},{c}}');
+   hstore_to_json    
+---------------------
+ ["a", ["b"], ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b},c}');
+   hstore_to_json    
+---------------------
+ [["a"], ["b"], "c"]
+(1 row)
+
+SELECT	hstore_to_json('{{a},b,{c}}');
+   hstore_to_json    
+---------------------
+ [["a"], "b", ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b=>1}}');
+ hstore_to_json  
+-----------------
+ ["a", {"b": 1}]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b=>1}}');
+  hstore_to_json   
+-------------------
+ [["a"], {"b": 1}]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b=>1},{c}}');
+      hstore_to_json      
+--------------------------
+ [["a"], {"b": 1}, ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('a');
+ hstore_to_json 
+----------------
+ "a"
+(1 row)
+
+SELECT	hstore_to_json('{a}');
+ hstore_to_json 
+----------------
+ ["a"]
+(1 row)
+
+SELECT	hstore_to_json('');
+ hstore_to_json 
+----------------
+ {}
+(1 row)
+
+SELECT	hstore_to_json('{}');
+ hstore_to_json 
+----------------
+ {}
+(1 row)
+
+SELECT hstore_to_json('"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore);
+                                                     hstore_to_json                                                      
+-------------------------------------------------------------------------------------------------------------------------
+ {"aa": ["a", "aaa"], "qq": {"a": "12", "b": "16", "c": ["c1", "c2", ["c3"], {"c4": 4}], "d": {"d1": "d1", "d2": "d2"}}}
+(1 row)
+
+--
+SELECT 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'ff', 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'qq', 
+	   ('ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'Y') IS NULL AS t, 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'x'; 
+     ?column?     | ?column? | t | ?column? 
+------------------+----------+---+----------
+ "a"=>12, "b"=>16 | 123      | t | [1, 2]
+(1 row)
+
+SELECT '[ a, b, c, d]'::hstore -> 'a';
+ ?column? 
+----------
+ a
+(1 row)
+
+--
+CREATE TABLE testtype (i int, h hstore, a int[], j json);
+INSERT INTO testtype VALUES (1, 'a=>1', '{1,2,3}', '{"x": 2}');
+SELECT populate_record(v, 'i=>2'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""a""=>1","{1,2,3}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, a=>{7,8,9}'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""a""=>1","{7,8,9}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""b""=>3","{7,8,9}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}'::hstore) FROM testtype v;
+                populate_record                
+-----------------------------------------------
+ (2,"""b""=>3","{7,8,9}","{""a"": [1, 2, 3]}")
+(1 row)
+
+--complex delete
+SELECT 'b=>{a,c}'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>1'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT '[2,3,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[a,2,3,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[a,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>1'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT ''::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '{a, 1 , b,2, c,3}'::hstore - ARRAY['d','b'];
+      ?column?       
+---------------------
+ ["a", 1, 2, "c", 3]
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v'::hstore;
+       ?column?        
+-----------------------
+ "a"=>[1, 2], "b"=>"c"
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>23'::hstore;
+       ?column?        
+-----------------------
+ "a"=>[1, 2], "b"=>"c"
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>{1,2}'::hstore;
+            ?column?            
+--------------------------------
+ "a"=>[1, 2], "b"=>"c", "v"=>23
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'a=>{1,2}'::hstore;
+     ?column?      
+-------------------
+ "b"=>"c", "v"=>23
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v'::hstore;
+          ?column?           
+-----------------------------
+ ["a", [1, 2], 23, "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v=>23'::hstore;
+        ?column?         
+-------------------------
+ ["a", [1, 2], "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,23]'::hstore;
+        ?column?         
+-------------------------
+ ["a", [1, 2], "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,{1,2}]'::hstore;
+      ?column?       
+---------------------
+ ["a", 23, "b", "c"]
+(1 row)
+
+--joining
+SELECT 'aa=>1 , b=>2, cq=>3'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+                   ?column?                    
+-----------------------------------------------
+ "1"=>2, "b"=>"g", "aa"=>1, "cq"=>"l", "fg"=>f
+(1 row)
+
+SELECT '{aa,1 , b,2, cq,3}'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+                            ?column?                            
+----------------------------------------------------------------
+ ["aa", 1, "b", 2, "cq", 3, "cq", "l", "b", "g", "fg", f, 1, 2]
+(1 row)
+
+--slice
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
+   slice_array    
+------------------
+ {NULL,NULL,NULL}
+(1 row)
+
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+   slice_array    
+------------------
+ {NULL,NULL,NULL}
+(1 row)
+
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['b','c']);
+ slice_array 
+-------------
+ {2,3}
+(1 row)
+
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+ slice_array 
+-------------
+ {b,c}
+(1 row)
+
+SELECT slice_array(hstore 'aa=>1, b=>{2=>1}, c=>{1,2}', ARRAY['b','c']);
+      slice_array      
+-----------------------
+ {"\"2\"=>1","[1, 2]"}
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['g','h','i']);
+ slice 
+-------
+ 
+(1 row)
+
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+ slice 
+-------
+ 
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['b','c']);
+     slice      
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+   slice    
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>{2=>1}, c=>{1,2}}', ARRAY['b','c']);
+           slice            
+----------------------------
+ "b"=>{"2"=>1}, "c"=>[1, 2]
+(1 row)
+
+--to array
+SELECT %% 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL';
+                ?column?                
+----------------------------------------
+ {b,"[\"a\", \"n\"]",aa,1,cq,l,fg,NULL}
+(1 row)
+
+SELECT %% '{aa,1, cq,l, b,g, fg,NULL}';
+        ?column?         
+-------------------------
+ {aa,1,cq,l,b,g,fg,NULL}
+(1 row)
+
+SELECT hstore_to_matrix( 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL');
+                hstore_to_matrix                
+------------------------------------------------
+ {{b,"[\"a\", \"n\"]"},{aa,1},{cq,l},{fg,NULL}}
+(1 row)
+
+SELECT hstore_to_matrix( '{aa,1, cq,l, b,g, fg,NULL}');
+        hstore_to_matrix         
+---------------------------------
+ {{aa,1},{cq,l},{b,g},{fg,NULL}}
+(1 row)
+
+--contains
+SELECT 'a=>b'::hstore @> 'a=>b, c=>b';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>b'::hstore @> 'a=>b';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{2,1}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1=>2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1=>2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{a,b}'::hstore @> '{a,b, c,b}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{a,b, c,b}'::hstore @> '{a,b}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{a,b, c,{1,2}}'::hstore @> '{a,{1,2}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{a,b, c,{1,2}}'::hstore @> '{b,{1,2}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{3}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{c=>3}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},3}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+-- %>
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'n';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'a';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'b';
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'c';
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd';
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd' %> '1';
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[1,2,3,{a,b}]'::hstore %> '1';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '["1",2,3,{a,b}]'::hstore %> '1';
+ ?column? 
+----------
+ "1"
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 3;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 2;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 1;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 0;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 3;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 2;
+ ?column? 
+----------
+ "c"
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 1;
+ ?column? 
+----------
+ "b"
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 0;
+ ?column? 
+----------
+ "a"
+(1 row)
+
+-- ->
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 3;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 2;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 1;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 0;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 3;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 2;
+ ?column? 
+----------
+ c
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 1;
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 0;
+ ?column? 
+----------
+ a
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -6;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -5;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -4;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -3;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -2;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -1;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -6;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -5;
+ ?column? 
+----------
+ a
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -4;
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -3;
+ ?column? 
+----------
+ c
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -2;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -1;
+ ?column? 
+----------
+ 
+(1 row)
+
+-- #>
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{a}';
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c}';
+ ?column?  
+-----------
+ [1, 2, 3]
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 0}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 1}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 2}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 3}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -1}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -2}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -3}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -4}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{0}';
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{3}';
+ ?column? 
+----------
+ [3, 4]
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4}';
+  ?column?   
+-------------
+ "5"=>"five"
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4,5}';
+ ?column? 
+----------
+ five
+(1 row)
+
+-- #%>
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{a}';
+ ?column? 
+----------
+ "b"
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c}';
+ ?column?  
+-----------
+ [1, 2, 3]
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 0}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 1}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 2}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 3}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -1}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -2}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -3}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -4}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{0}';
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{3}';
+ ?column? 
+----------
+ [3, 4]
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4}';
+  ?column?   
+-------------
+ "5"=>"five"
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4,5}';
+ ?column? 
+----------
+ "five"
+(1 row)
+
+-- ?
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 5;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 0;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 5;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 0;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -6;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -5;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -6;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -5;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{0}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{a}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{b}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 0}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 1}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 2}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 3}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -1}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -2}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -3}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -4}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -5}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{0}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{3}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4,5}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+--deep delete
+SELECT 'a=>1'::hstore #- '{x}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{a}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{NULL}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a}';
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b}';
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c}';
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{x,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{a,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{NULL,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT '[a]'::hstore #- '{2}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a]'::hstore #- '{1}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a]'::hstore #- '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore #- '{-1}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore #- '{-2}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{3}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{2}';
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{1}';
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{0}';
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-1}';
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-2}';
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-3}';
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-4}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{0,0}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{x}';
+                             ?column?                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{a}';
+                         ?column?                          
+-----------------------------------------------------------
+ "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b}';
+                       ?column?                       
+------------------------------------------------------
+ "a"=>1, "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c}';
+                      ?column?                      
+----------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d}';
+                   ?column?                    
+-----------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, 0}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}' #- '{b, -1}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>NULL, "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 1}';
+                           ?column?                            
+---------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>NULL, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 2}';
+                             ?column?                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, -2}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 1}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>NULL}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}' #- '{d, 1, 0}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>NULL}, "n"=>NULL
+(1 row)
+
+-- delete(int)
+SELECT '[a,b,c]'::hstore - 3;
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 2;
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 1;
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 0;
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -1;
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -2;
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -3;
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -4;
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 3;
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 2;
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 1;
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 0;
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -1;
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -2;
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -3;
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -4;
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+--replace
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{1,2,3}');
+                                replace                                 
+------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>[1, 2, 3]
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b,-1}', '{1,2,3}');
+                                  replace                                  
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, [1, 2, 3]], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1,0}', '{1,2,3}');
+                                  replace                                  
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[[1, 2, 3], 3]}, "n"=>NULL
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,NULL,0}', '{1,2,3}');
+                              replace                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+--deep concat
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'n=>not_null');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>"not_null"
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'n=>not_null');
+                                  concat_path                                   
+--------------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>{"n"=>"not_null"}
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'not_null');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>"not_null"
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{not_null}');
+                                concat_path                                
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>["not_null"]
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'b=>{3,4}');
+                            concat_path                            
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[3, 4], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b}', '{3,4}');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2, 3, 4], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '{4,5}');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3, 4, 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '4=>5');
+                                concat_path                                
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3, "4", 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d}', '2=>{4,5}');
+                                  concat_path                                   
+--------------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3], "2"=>[4, 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{NULL,1}', '4=>5');
+                            concat_path                            
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+--cast 
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::text)::hstore AS err;
+ERROR:  bad hstore representation
+DETAIL:  syntax error, unexpected STRING_P, expecting '}' or ',' at end of input
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json)::hstore AS ok;
+                         ok                         
+----------------------------------------------------
+ "f2"=>{"f3"=>1}, "f4"=>{"f5"=>99, "f6"=>"stringy"}
+(1 row)
+
+--hvals
+SELECT q->'tags' FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore) AS q;
+ ?column? 
+----------
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+                      q                       
+----------------------------------------------
+ [{"sh"=>2, "tags"=>1}, {"sh"=>4, "tags"=>3}]
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+ "sh"=>4, "tags"=>3
+(2 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+ q 
+---
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+                                q                                
+-----------------------------------------------------------------
+ "1"=>"first", "a"=>{"b"=>"c", "c"=>"b"}, "b"=>[1, 2], "c"=>"cc"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+    q    
+---------
+ "first"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+         q          
+--------------------
+ "b"=>"c", "c"=>"b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+   q    
+--------
+ [1, 2]
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+  q   
+------
+ "cc"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "first"
+ "b"=>"c", "c"=>"b"
+ [1, 2]
+ "cc"
+(4 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+  q  
+-----
+ "b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+  q  
+-----
+ "b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+ q 
+---
+ 1
+ 2
+(2 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+ q 
+---
+ 2
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+    q    
+---------
+ "first"
+ 2
+(2 rows)
+
+--svals path
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+                      q                       
+----------------------------------------------
+ [{"sh"=>2, "tags"=>1}, {"sh"=>4, "tags"=>3}]
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+ "sh"=>4, "tags"=>3
+(2 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+ q 
+---
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+                                q                                
+-----------------------------------------------------------------
+ "1"=>"first", "a"=>{"b"=>"c", "c"=>"b"}, "b"=>[1, 2], "c"=>"cc"
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+   q   
+-------
+ first
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+         q          
+--------------------
+ "b"=>"c", "c"=>"b"
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+   q    
+--------
+ [1, 2]
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+ q  
+----
+ cc
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ first
+ "b"=>"c", "c"=>"b"
+ [1, 2]
+ cc
+(4 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+ q 
+---
+ b
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+ q 
+---
+ b
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+ q 
+---
+ 1
+ 2
+(2 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+ q 
+---
+ 2
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+   q   
+-------
+ first
+ 2
+(2 rows)
+
+--each
+SELECT * FROM each('a=>b, c=>cc'::hstore) AS q;
+ key | value 
+-----+-------
+ a   | b
+ c   | cc
+(2 rows)
+
+SELECT * FROM each('[a, b, c, cc]'::hstore) AS q;
+ key | value 
+-----+-------
+     | a
+     | b
+     | c
+     | cc
+(4 rows)
+
+SELECT * FROM each('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+ key |              value               
+-----+----------------------------------
+ 1   | first
+ a   | "1"=>"first", "b"=>"c", "c"=>"b"
+ b   | [1, 2]
+ c   | cc
+ n   | 
+(5 rows)
+
+SELECT * FROM each_hstore('a=>b, c=>cc'::hstore) AS q;
+ key | value 
+-----+-------
+ a   | "b"
+ c   | "cc"
+(2 rows)
+
+SELECT * FROM each_hstore('[a, b, c, cc]'::hstore) AS q;
+ key | value 
+-----+-------
+     | "a"
+     | "b"
+     | "c"
+     | "cc"
+(4 rows)
+
+SELECT * FROM each_hstore('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+ key |              value               
+-----+----------------------------------
+ 1   | "first"
+ a   | "1"=>"first", "b"=>"c", "c"=>"b"
+ b   | [1, 2]
+ c   | "cc"
+ n   | 
+(5 rows)
+
+--decoration
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]]'::hstore AS h, '[a, {b=>c}, [c, d, e]]'::hstore AS a;
+                  h                   |                 a                  
+--------------------------------------+------------------------------------
+ "a"=>1, "b"=>{"c"=>3}, "d"=>[4, [5]] | ["a", {"b"=>"c"}, ["c", "d", "e"]]
+(1 row)
+
+SET hstore.pretty_print = true;
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]], e=>[1,2,3,4], f=>g, g=>j'::hstore AS h, 
+	   '[a, {b=>c, c=>d}, [c, d, e, [1,2], h, {f=>g, g=>f}]]'::hstore AS a;
+     h      |           a            
+------------+------------------------
+ "a"=>1,   +| [                     +
+ "b"=>     +|     "a",              +
+ {         +|     {                 +
+     "c"=>3+|         "b"=>"c",     +
+ },        +|         "c"=>"d"      +
+ "d"=>     +|     },                +
+ [         +|     [                 +
+     4,    +|         "c",          +
+     [     +|         "d",          +
+         5 +|         "e",          +
+     ]     +|         [             +
+ ],        +|             1,        +
+ "e"=>     +|             2         +
+ [         +|         ],            +
+     1,    +|         "h",          +
+     2,    +|         {             +
+     3,    +|             "f"=>"g", +
+     4     +|             "g"=>f    +
+ ],        +|         }             +
+ "f"=>"g", +|     ]                 +
+ "g"=>"j"   | ]
+(1 row)
+
+RESET hstore.pretty_print;
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore);
+                             hstore_print                              
+-----------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>[1, 2, 3, "3", "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true );
+                           hstore_print                            
+-------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>[1, 2, 3, 3, "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true );
+                              hstore_print                               
+-------------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>[1, 2, 3, "3", "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, array_curly_braces := true );
+                             hstore_print                              
+-----------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>{1, 2, 3, "3", "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": "f", "123": "string", "arr": [1, 2, 3, "3", "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, loose := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": false, "123": "string", "arr": [1, 2, 3, 3, "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, root_hash_decorated := true );
+                                 hstore_print                                  
+-------------------------------------------------------------------------------
+ {"a": true, "f": true, "t": "f", "123": "string", "arr": [1, 2, 3, "3", "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, array_curly_braces := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": "f", "123": "string", "arr": {1, 2, 3, "3", "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, root_hash_decorated := true );
+                            hstore_print                             
+---------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>[1, 2, 3, 3, "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, array_curly_braces := true );
+                           hstore_print                            
+-------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>{1, 2, 3, 3, "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true );
+                              hstore_print                               
+-------------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>{1, 2, 3, "3", "x"}}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true, loose := true);
+                            hstore_print                             
+---------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>{1, 2, 3, 3, "x"}}
+(1 row)
+
diff --git a/contrib/hstore/expected/types.out b/contrib/hstore/expected/types.out
new file mode 100644
index 0000000..8233d52
--- /dev/null
+++ b/contrib/hstore/expected/types.out
@@ -0,0 +1,732 @@
+SELECT '"foo"=>true'::hstore;
+  hstore  
+----------
+ "foo"=>t
+(1 row)
+
+SELECT 'foo=>true'::hstore;
+  hstore  
+----------
+ "foo"=>t
+(1 row)
+
+SELECT '"true"=>true'::hstore;
+  hstore   
+-----------
+ "true"=>t
+(1 row)
+
+SELECT 'true=>true'::hstore;
+  hstore   
+-----------
+ "true"=>t
+(1 row)
+
+SELECT '"t"=>true'::hstore;
+ hstore 
+--------
+ "t"=>t
+(1 row)
+
+SELECT 't=>true'::hstore;
+ hstore 
+--------
+ "t"=>t
+(1 row)
+
+SELECT '"false"=>true'::hstore;
+   hstore   
+------------
+ "false"=>t
+(1 row)
+
+SELECT 'false=>true'::hstore;
+   hstore   
+------------
+ "false"=>t
+(1 row)
+
+SELECT '"f"=>true'::hstore;
+ hstore 
+--------
+ "f"=>t
+(1 row)
+
+SELECT 'f=>true'::hstore;
+ hstore 
+--------
+ "f"=>t
+(1 row)
+
+SELECT '"foo"=>false'::hstore;
+  hstore  
+----------
+ "foo"=>f
+(1 row)
+
+SELECT 'foo=>false'::hstore;
+  hstore  
+----------
+ "foo"=>f
+(1 row)
+
+SELECT '"false"=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT 'false=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT '"t"=>false'::hstore;
+ hstore 
+--------
+ "t"=>f
+(1 row)
+
+SELECT 't=>false'::hstore;
+ hstore 
+--------
+ "t"=>f
+(1 row)
+
+SELECT '"false"=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT 'false=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT '"f"=>false'::hstore;
+ hstore 
+--------
+ "f"=>f
+(1 row)
+
+SELECT 'f=>false'::hstore;
+ hstore 
+--------
+ "f"=>f
+(1 row)
+
+SELECT '"1"=>x'::hstore;
+  hstore  
+----------
+ "1"=>"x"
+(1 row)
+
+SELECT '1=>x'::hstore;
+  hstore  
+----------
+ "1"=>"x"
+(1 row)
+
+SELECT 'foo=>1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>1.'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>1.0'::hstore;
+   hstore   
+------------
+ "foo"=>1.0
+(1 row)
+
+SELECT 'foo=>1.01'::hstore;
+   hstore    
+-------------
+ "foo"=>1.01
+(1 row)
+
+SELECT 'foo=>1.01e'::hstore;
+     hstore     
+----------------
+ "foo"=>"1.01e"
+(1 row)
+
+SELECT 'foo=>1.01e1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>1.01e+1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>1.01e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>0.101
+(1 row)
+
+SELECT 'foo=>.1'::hstore;
+   hstore   
+------------
+ "foo"=>0.1
+(1 row)
+
+SELECT 'foo=>.1e'::hstore;
+    hstore    
+--------------
+ "foo"=>".1e"
+(1 row)
+
+SELECT 'foo=>.1e1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>.1e+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+1.'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+1.0'::hstore;
+   hstore   
+------------
+ "foo"=>1.0
+(1 row)
+
+SELECT 'foo=>+1.01'::hstore;
+   hstore    
+-------------
+ "foo"=>1.01
+(1 row)
+
+SELECT 'foo=>+1.01e'::hstore;
+     hstore      
+-----------------
+ "foo"=>"+1.01e"
+(1 row)
+
+SELECT 'foo=>+1.01e1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>+1.01e+1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>+1.01e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>0.101
+(1 row)
+
+SELECT 'foo=>+.1'::hstore;
+   hstore   
+------------
+ "foo"=>0.1
+(1 row)
+
+SELECT 'foo=>+.1e'::hstore;
+    hstore     
+---------------
+ "foo"=>"+.1e"
+(1 row)
+
+SELECT 'foo=>+.1e1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+.1e+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>-1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-1.'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-1.0'::hstore;
+   hstore    
+-------------
+ "foo"=>-1.0
+(1 row)
+
+SELECT 'foo=>-1.01'::hstore;
+    hstore    
+--------------
+ "foo"=>-1.01
+(1 row)
+
+SELECT 'foo=>-1.01e'::hstore;
+     hstore      
+-----------------
+ "foo"=>"-1.01e"
+(1 row)
+
+SELECT 'foo=>-1.01e1'::hstore;
+    hstore    
+--------------
+ "foo"=>-10.1
+(1 row)
+
+SELECT 'foo=>-1.01e+1'::hstore;
+    hstore    
+--------------
+ "foo"=>-10.1
+(1 row)
+
+SELECT 'foo=>-1.01e-1'::hstore;
+    hstore     
+---------------
+ "foo"=>-0.101
+(1 row)
+
+SELECT 'foo=>-.1'::hstore;
+   hstore    
+-------------
+ "foo"=>-0.1
+(1 row)
+
+SELECT 'foo=>-.1e'::hstore;
+    hstore     
+---------------
+ "foo"=>"-.1e"
+(1 row)
+
+SELECT 'foo=>-.1e1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-.1e+1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-.1e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>-0.01
+(1 row)
+
+SELECT 'foo=>1e2000'::hstore;
+     hstore      
+-----------------
+ "foo"=>"1e2000"
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'foo';
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'bar';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 0;
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 1;
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'foo';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'bar';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 0;
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 1;
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 0}';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 1}';
+    ?column?    
+----------------
+ 0.000000000001
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'foo';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'foo';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'foo';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 1;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'foo';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 1;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 1}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT hstore_typeof('a=>b') AS hash;
+ hash 
+------
+ hash
+(1 row)
+
+SELECT hstore_typeof('{a=>b}') AS hash;
+ hash 
+------
+ hash
+(1 row)
+
+SELECT hstore_typeof('{a, b}') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('{{a=>b}}') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('[a, b]') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('') AS "NULL";
+ NULL 
+------
+ 
+(1 row)
+
+SELECT hstore_typeof('NULL') AS "null";
+ null 
+------
+ null
+(1 row)
+
+SELECT hstore_typeof('1.0') AS numeric;
+ numeric 
+---------
+ numeric
+(1 row)
+
+SELECT hstore_typeof('t') AS bool;
+ bool 
+------
+ bool
+(1 row)
+
+SELECT hstore_typeof('f') AS bool;
+ bool 
+------
+ bool
+(1 row)
+
+SELECT hstore('xxx', 't'::bool);
+  hstore  
+----------
+ "xxx"=>t
+(1 row)
+
+SELECT hstore('xxx', 'f'::bool);
+  hstore  
+----------
+ "xxx"=>f
+(1 row)
+
+SELECT hstore('xxx', 3.14);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore('xxx', 3.14::numeric);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore('xxx', '3.14'::numeric);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore(NULL);
+ hstore 
+--------
+ 
+(1 row)
+
+SELECT hstore('NULL');
+ hstore 
+--------
+ NULL
+(1 row)
+
+SELECT hstore('t'::bool) AS "true", hstore('f'::bool) AS "false";
+ true | false 
+------+-------
+ t    | f
+(1 row)
+
+SELECT hstore(3.14), hstore(3.14::numeric), hstore('3.14'::numeric);
+ hstore | hstore | hstore 
+--------+--------+--------
+ 3.14   | 3.14   | 3.14
+(1 row)
+
+SELECT hstore('xxx', 'foo=>t, bar=>3.14, zzz=>xxx'::hstore);
+                    hstore                    
+----------------------------------------------
+ "xxx"=>{"bar"=>3.14, "foo"=>t, "zzz"=>"xxx"}
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int2[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int4[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int8[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float4[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float8[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,f},{f,t,NULL}}'::bool[]);
+      array_to_hstore      
+---------------------------
+ [[t, t, f], [f, t, NULL]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::text[]);
+           array_to_hstore           
+-------------------------------------
+ [["1", "1", "4"], ["23", "3", "5"]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::varchar[]);
+           array_to_hstore           
+-------------------------------------
+ [["1", "1", "4"], ["23", "3", "5"]]
+(1 row)
+
+SELECT array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]);
+                       array_to_hstore                       
+-------------------------------------------------------------
+ [[[1, 11], [1, 1], [4, 41], [23, 231]], [[3, 31], [5, 51]]]
+(1 row)
+
+SELECT hstore('array', array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]));
+                                hstore                                
+----------------------------------------------------------------------
+ "array"=>[[[1, 11], [1, 1], [4, 41], [23, 231]], [[3, 31], [5, 51]]]
+(1 row)
+
diff --git a/contrib/hstore/hstore--1.2--1.3.sql b/contrib/hstore/hstore--1.2--1.3.sql
new file mode 100644
index 0000000..b913a08
--- /dev/null
+++ b/contrib/hstore/hstore--1.2--1.3.sql
@@ -0,0 +1,338 @@
+/* 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 FUNCTION hstore2jsonb(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore2jsonb'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS jsonb)
+  WITH FUNCTION hstore2jsonb(hstore);
+
+CREATE FUNCTION jsonb2hstore(jsonb)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'jsonb2hstore'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (jsonb AS hstore)
+  WITH FUNCTION jsonb2hstore(jsonb);
+
diff --git a/contrib/hstore/hstore--1.2.sql b/contrib/hstore/hstore--1.2.sql
deleted file mode 100644
index f415a72..0000000
--- a/contrib/hstore/hstore--1.2.sql
+++ /dev/null
@@ -1,537 +0,0 @@
-/* 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
-
-CREATE TYPE hstore;
-
-CREATE FUNCTION hstore_in(cstring)
-RETURNS hstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_out(hstore)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_recv(internal)
-RETURNS hstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_send(hstore)
-RETURNS bytea
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE TYPE hstore (
-        INTERNALLENGTH = -1,
-        INPUT = hstore_in,
-        OUTPUT = hstore_out,
-        RECEIVE = hstore_recv,
-        SEND = hstore_send,
-        STORAGE = extended
-);
-
-CREATE FUNCTION hstore_version_diag(hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_version_diag'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION fetchval(hstore,text)
-RETURNS text
-AS 'MODULE_PATHNAME','hstore_fetchval'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR -> (
-	LEFTARG = hstore,
-	RIGHTARG = text,
-	PROCEDURE = fetchval
-);
-
-CREATE FUNCTION slice_array(hstore,text[])
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_slice_to_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR -> (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = slice_array
-);
-
-CREATE FUNCTION slice(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_slice_to_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION isexists(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION exist(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists'
-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'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?| (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = exists_any,
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION exists_all(hstore,text[])
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_all'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?& (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = exists_all,
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION isdefined(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_defined'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION defined(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_defined'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = text,
-	PROCEDURE = delete
-);
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = delete
-);
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = delete
-);
-
-CREATE FUNCTION hs_concat(hstore,hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_concat'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR || (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_concat
-);
-
-CREATE FUNCTION hs_contains(hstore,hstore)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_contains'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hs_contained(hstore,hstore)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_contained'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR @> (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contains,
-	COMMUTATOR = '<@',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE OPERATOR <@ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contained,
-	COMMUTATOR = '@>',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
--- obsolete:
-CREATE OPERATOR @ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contains,
-	COMMUTATOR = '~',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contained,
-	COMMUTATOR = '@',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION tconvert(text,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_text'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_text'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text[],text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_arrays'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null)
-
-CREATE FUNCTION hstore(text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_array'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE CAST (text[] AS hstore)
-  WITH FUNCTION hstore(text[]);
-
-CREATE FUNCTION hstore_to_json(hstore)
-RETURNS json
-AS 'MODULE_PATHNAME', 'hstore_to_json'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE CAST (hstore AS json)
-  WITH FUNCTION hstore_to_json(hstore);
-
-CREATE FUNCTION hstore_to_json_loose(hstore)
-RETURNS json
-AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION hstore(record)
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_record'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype)
-
-CREATE FUNCTION hstore_to_array(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_to_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %% (
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_to_array
-);
-
-CREATE FUNCTION hstore_to_matrix(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_to_matrix'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %# (
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_to_matrix
-);
-
-CREATE FUNCTION akeys(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_akeys'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION avals(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_avals'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION skeys(hstore)
-RETURNS setof text
-AS 'MODULE_PATHNAME','hstore_skeys'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION svals(hstore)
-RETURNS setof text
-AS 'MODULE_PATHNAME','hstore_svals'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION each(IN hs hstore,
-    OUT key text,
-    OUT value text)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME','hstore_each'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION populate_record(anyelement,hstore)
-RETURNS anyelement
-AS 'MODULE_PATHNAME', 'hstore_populate_record'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore)
-
-CREATE OPERATOR #= (
-	LEFTARG = anyelement,
-	RIGHTARG = hstore,
-	PROCEDURE = populate_record
-);
-
--- btree support
-
-CREATE FUNCTION hstore_eq(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_eq'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_ne(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_ne'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_gt(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_gt'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_ge(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_ge'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_lt(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_lt'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_le(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_le'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_cmp(hstore,hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_cmp'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR = (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_eq,
-       COMMUTATOR = =,
-       NEGATOR = <>,
-       RESTRICT = eqsel,
-       JOIN = eqjoinsel,
-       MERGES,
-       HASHES
-);
-CREATE OPERATOR <> (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_ne,
-       COMMUTATOR = <>,
-       NEGATOR = =,
-       RESTRICT = neqsel,
-       JOIN = neqjoinsel
-);
-
--- the comparison operators have funky names (and are undocumented)
--- in an attempt to discourage anyone from actually using them. they
--- only exist to support the btree opclass
-
-CREATE OPERATOR #<# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_lt,
-       COMMUTATOR = #>#,
-       NEGATOR = #>=#,
-       RESTRICT = scalarltsel,
-       JOIN = scalarltjoinsel
-);
-CREATE OPERATOR #<=# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_le,
-       COMMUTATOR = #>=#,
-       NEGATOR = #>#,
-       RESTRICT = scalarltsel,
-       JOIN = scalarltjoinsel
-);
-CREATE OPERATOR #># (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_gt,
-       COMMUTATOR = #<#,
-       NEGATOR = #<=#,
-       RESTRICT = scalargtsel,
-       JOIN = scalargtjoinsel
-);
-CREATE OPERATOR #>=# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_ge,
-       COMMUTATOR = #<=#,
-       NEGATOR = #<#,
-       RESTRICT = scalargtsel,
-       JOIN = scalargtjoinsel
-);
-
-CREATE OPERATOR CLASS btree_hstore_ops
-DEFAULT FOR TYPE hstore USING btree
-AS
-	OPERATOR	1	#<# ,
-	OPERATOR	2	#<=# ,
-	OPERATOR	3	= ,
-	OPERATOR	4	#>=# ,
-	OPERATOR	5	#># ,
-	FUNCTION	1	hstore_cmp(hstore,hstore);
-
--- hash support
-
-CREATE FUNCTION hstore_hash(hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_hash'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR CLASS hash_hstore_ops
-DEFAULT FOR TYPE hstore USING hash
-AS
-	OPERATOR	1	= ,
-	FUNCTION	1	hstore_hash(hstore);
-
--- GiST support
-
-CREATE TYPE ghstore;
-
-CREATE FUNCTION ghstore_in(cstring)
-RETURNS ghstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION ghstore_out(ghstore)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE TYPE ghstore (
-        INTERNALLENGTH = -1,
-        INPUT = ghstore_in,
-        OUTPUT = ghstore_out
-);
-
-CREATE FUNCTION ghstore_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_union(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_same(internal, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR CLASS gist_hstore_ops
-DEFAULT FOR TYPE hstore USING gist
-AS
-	OPERATOR        7       @> ,
-	OPERATOR        9       ?(hstore,text) ,
-	OPERATOR        10      ?|(hstore,text[]) ,
-	OPERATOR        11      ?&(hstore,text[]) ,
-        --OPERATOR        8       <@ ,
-        OPERATOR        13      @ ,
-        --OPERATOR        14      ~ ,
-        FUNCTION        1       ghstore_consistent (internal, internal, int, oid, internal),
-        FUNCTION        2       ghstore_union (internal, internal),
-        FUNCTION        3       ghstore_compress (internal),
-        FUNCTION        4       ghstore_decompress (internal),
-        FUNCTION        5       ghstore_penalty (internal, internal, internal),
-        FUNCTION        6       ghstore_picksplit (internal, internal),
-        FUNCTION        7       ghstore_same (internal, internal, internal),
-        STORAGE         ghstore;
-
--- GIN support
-
-CREATE FUNCTION gin_extract_hstore(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR CLASS gin_hstore_ops
-DEFAULT FOR TYPE hstore USING gin
-AS
-	OPERATOR        7       @>,
-	OPERATOR        9       ?(hstore,text),
-	OPERATOR        10      ?|(hstore,text[]),
-	OPERATOR        11      ?&(hstore,text[]),
-	FUNCTION        1       bttextcmp(text,text),
-	FUNCTION        2       gin_extract_hstore(internal, internal),
-	FUNCTION        3       gin_extract_hstore_query(internal, internal, int2, internal, internal),
-	FUNCTION        4       gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
-	STORAGE         text;
diff --git a/contrib/hstore/hstore--1.3.sql b/contrib/hstore/hstore--1.3.sql
new file mode 100644
index 0000000..153cb74
--- /dev/null
+++ b/contrib/hstore/hstore--1.3.sql
@@ -0,0 +1,854 @@
+/* contrib/hstore/hstore--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION hstore" to load this file. \quit
+
+CREATE TYPE hstore;
+
+CREATE FUNCTION hstore_in(cstring)
+RETURNS hstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_out(hstore)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_recv(internal)
+RETURNS hstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_send(hstore)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE hstore (
+        INTERNALLENGTH = -1,
+        INPUT = hstore_in,
+        OUTPUT = hstore_out,
+        RECEIVE = hstore_recv,
+        SEND = hstore_send,
+        STORAGE = extended
+);
+
+CREATE FUNCTION hstore_version_diag(hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_version_diag'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION fetchval(hstore,text)
+RETURNS text
+AS 'MODULE_PATHNAME','hstore_fetchval'
+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_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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR -> (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = slice_array
+);
+
+CREATE FUNCTION slice(hstore,text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_slice_to_hstore'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION isexists(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION exist(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ? (
+	LEFTARG = hstore,
+	RIGHTARG = text,
+	PROCEDURE = exist,
+	RESTRICT = contsel,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ?| (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = exists_any,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION exists_all(hstore,text[])
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists_all'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ?& (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = exists_all,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION isdefined(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_defined'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION defined(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_defined'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,text)
+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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,hstore)
+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[],
+	PROCEDURE = delete
+);
+
+CREATE OPERATOR - (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR || (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hs_contained(hstore,hstore)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_contained'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR @> (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contains,
+	COMMUTATOR = '<@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contained,
+	COMMUTATOR = '@>',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+-- obsolete:
+CREATE OPERATOR @ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contains,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contained,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION tconvert(text,text)
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_from_text'
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
+
+CREATE FUNCTION hstore(text,text)
+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'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null)
+
+CREATE FUNCTION hstore(text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_array'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (text[] AS hstore)
+  WITH FUNCTION hstore(text[]);
+
+CREATE FUNCTION hstore_to_json(hstore)
+RETURNS json
+AS 'MODULE_PATHNAME', 'hstore_to_json'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS json)
+  WITH FUNCTION hstore_to_json(hstore);
+
+CREATE FUNCTION hstore2jsonb(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore2jsonb'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS jsonb)
+  WITH FUNCTION hstore2jsonb(hstore);
+
+CREATE FUNCTION jsonb2hstore(jsonb)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'jsonb2hstore'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (jsonb AS hstore)
+  WITH FUNCTION jsonb2hstore(jsonb);
+
+CREATE FUNCTION hstore_to_json_loose(hstore)
+RETURNS json
+AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION hstore(record)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_record'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype)
+
+CREATE FUNCTION hstore_to_array(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_to_array'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR %% (
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_to_array
+);
+
+CREATE FUNCTION hstore_to_matrix(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_to_matrix'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR %# (
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_to_matrix
+);
+
+CREATE FUNCTION akeys(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_akeys'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION avals(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_avals'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION skeys(hstore)
+RETURNS setof text
+AS 'MODULE_PATHNAME','hstore_skeys'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION svals(hstore)
+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)
+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'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore)
+
+CREATE OPERATOR #= (
+	LEFTARG = anyelement,
+	RIGHTARG = hstore,
+	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)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_eq'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_ne(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_ne'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_gt(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_gt'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_ge(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_ge'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_lt(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_lt'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_le(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_le'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_cmp(hstore,hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_cmp'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR = (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_eq,
+       COMMUTATOR = =,
+       NEGATOR = <>,
+       RESTRICT = eqsel,
+       JOIN = eqjoinsel,
+       MERGES,
+       HASHES
+);
+CREATE OPERATOR <> (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_ne,
+       COMMUTATOR = <>,
+       NEGATOR = =,
+       RESTRICT = neqsel,
+       JOIN = neqjoinsel
+);
+
+-- the comparison operators have funky names (and are undocumented)
+-- in an attempt to discourage anyone from actually using them. they
+-- only exist to support the btree opclass
+
+CREATE OPERATOR #<# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_lt,
+       COMMUTATOR = #>#,
+       NEGATOR = #>=#,
+       RESTRICT = scalarltsel,
+       JOIN = scalarltjoinsel
+);
+CREATE OPERATOR #<=# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_le,
+       COMMUTATOR = #>=#,
+       NEGATOR = #>#,
+       RESTRICT = scalarltsel,
+       JOIN = scalarltjoinsel
+);
+CREATE OPERATOR #># (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_gt,
+       COMMUTATOR = #<#,
+       NEGATOR = #<=#,
+       RESTRICT = scalargtsel,
+       JOIN = scalargtjoinsel
+);
+CREATE OPERATOR #>=# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_ge,
+       COMMUTATOR = #<=#,
+       NEGATOR = #<#,
+       RESTRICT = scalargtsel,
+       JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR CLASS btree_hstore_ops
+DEFAULT FOR TYPE hstore USING btree
+AS
+	OPERATOR	1	#<# ,
+	OPERATOR	2	#<=# ,
+	OPERATOR	3	= ,
+	OPERATOR	4	#>=# ,
+	OPERATOR	5	#># ,
+	FUNCTION	1	hstore_cmp(hstore,hstore);
+
+-- hash support
+
+CREATE FUNCTION hstore_hash(hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_hash'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR CLASS hash_hstore_ops
+DEFAULT FOR TYPE hstore USING hash
+AS
+	OPERATOR	1	= ,
+	FUNCTION	1	hstore_hash(hstore);
+
+-- GiST support
+
+CREATE TYPE ghstore;
+
+CREATE FUNCTION ghstore_in(cstring)
+RETURNS ghstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION ghstore_out(ghstore)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE ghstore (
+        INTERNALLENGTH = -1,
+        INPUT = ghstore_in,
+        OUTPUT = ghstore_out
+);
+
+CREATE FUNCTION ghstore_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_union(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_same(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR CLASS gist_hstore_ops
+DEFAULT FOR TYPE hstore USING gist
+AS
+	OPERATOR        7       @> ,
+	OPERATOR        9       ?(hstore,text) ,
+	OPERATOR        10      ?|(hstore,text[]) ,
+	OPERATOR        11      ?&(hstore,text[]) ,
+        --OPERATOR        8       <@ ,
+        OPERATOR        13      @ ,
+        --OPERATOR        14      ~ ,
+        FUNCTION        1       ghstore_consistent (internal, internal, int, oid, internal),
+        FUNCTION        2       ghstore_union (internal, internal),
+        FUNCTION        3       ghstore_compress (internal),
+        FUNCTION        4       ghstore_decompress (internal),
+        FUNCTION        5       ghstore_penalty (internal, internal, internal),
+        FUNCTION        6       ghstore_picksplit (internal, internal),
+        FUNCTION        7       ghstore_same (internal, internal, internal),
+        STORAGE         ghstore;
+
+-- GIN support: default opclass
+
+CREATE FUNCTION gin_extract_hstore(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR CLASS gin_hstore_ops
+DEFAULT FOR TYPE hstore USING gin
+AS
+	OPERATOR        7       @>,
+	OPERATOR        9       ?(hstore,text),
+	OPERATOR        10      ?|(hstore,text[]),
+	OPERATOR        11      ?&(hstore,text[]),
+	FUNCTION        1       bttextcmp(text,text),
+	FUNCTION        2       gin_extract_hstore(internal, internal),
+	FUNCTION        3       gin_extract_hstore_query(internal, internal, int2, internal, internal),
+	FUNCTION        4       gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
+	STORAGE         text;
+
+-- 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;
+
+
+
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index 9daf5e2..dcc3b68 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,5 +1,5 @@
 # hstore extension
 comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/hstore'
 relocatable = true
diff --git a/contrib/hstore/hstore.h b/contrib/hstore/hstore.h
index 23c8a6f..197289b 100644
--- a/contrib/hstore/hstore.h
+++ b/contrib/hstore/hstore.h
@@ -4,9 +4,7 @@
 #ifndef __HSTORE_H__
 #define __HSTORE_H__
 
-#include "fmgr.h"
-#include "utils/array.h"
-
+#include "utils/jsonb.h"
 
 /*
  * HEntry: there is one of these for each key _and_ value in an hstore
@@ -15,158 +13,126 @@
  * by subtraction from the previous entry.	the ISFIRST flag lets us tell
  * whether there is a previous entry.
  */
-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]))
+
+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_ISCALAR is only used in send/recv */
+#define HENTRY_ISHASH		JENTRY_ISOBJECT
+#define HENTRY_ISARRAY		JENTRY_ISARRAY
+#define HENTRY_ISCALAR		JENTRY_ISCALAR
+
+#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_)
 
 /*
- * 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
+ * determined by the size of "endpos" (ie HENTRY_POSMASK)
  */
-#define HSTORE_MAX_KEY_LEN 0x3FFFFFFF
-#define HSTORE_MAX_VALUE_LEN 0x3FFFFFFF
+#define HSTORE_MAX_KEY_LEN 		HENTRY_POSMASK
+#define HSTORE_MAX_VALUE_LEN 	HENTRY_POSMASK
 
-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;
+typedef Jsonb HStore;
 
 /*
  * it's not possible to get more than 2^28 items into an hstore,
  * so we reserve the top few bits of the size field. See hstore_compat.c
  * for one reason why.	Some bits are left for future use here.
  */
-#define HS_FLAG_NEWVERSION 0x80000000
+#define HS_FLAG_NEWVERSION 		0x80000000
+#define HS_FLAG_ARRAY			JB_FLAG_ARRAY
+#define HS_FLAG_HASH			JB_FLAG_OBJECT
+#define HS_FLAG_SCALAR			JB_FLAG_SCALAR
 
-#define HS_COUNT(hsp_) ((hsp_)->size_ & 0x0FFFFFFF)
-#define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION)
+#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 HSHRDSIZE	(sizeof(HStore))
-#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) )
+/* DatumGetHStoreP includes support for reading old-format hstore values */
+extern HStore *hstoreUpgrade(Datum orig);
 
-/* note multiple evaluations of x */
-#define ARRPTR(x)		( (HEntry*) ( (HStore*)(x) + 1 ) )
-#define STRPTR(x)		( (char*)(ARRPTR(x) + HS_COUNT((HStore*)(x)) * 2) )
+#define DatumGetHStoreP(d) hstoreUpgrade(d)
 
-/* 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 PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x))
 
-/*
- * 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.
- */
+typedef JsonbPair HStorePair;
+typedef JsonbValue HStoreValue;
 
-/*
- * 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)
+/* JsonbValue.type renaming */
+#define hsvNull		jbvNull
+#define hsvString	jbvString
+#define hsvNumeric	jbvNumeric
+#define hsvBool		jbvBool
+#define hsvArray	jbvArray
+#define hsvHash		jbvHash
+#define hsvBinary	jbvBinary
 
 /*
- * add one key/item pair, from a Pairs structure, into an
- * under-construction hstore
+ * hstore support functions, they are mostly the same as jsonb
  */
-#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)
 
-/* DatumGetHStoreP includes support for reading old-format hstore values */
-extern HStore *hstoreUpgrade(Datum orig);
+#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 DatumGetHStoreP(d) hstoreUpgrade(d)
+#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))
 
-#define PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x))
+#define compareHStoreBinaryValue(a, b)				compareJsonbBinaryValue((a), (b))
+#define compareHStoreValue(a, b)					compareJsonbValue((a), (b))
 
+#define findUncompressedHStoreValueByValue(buffer, flags, lowbound, key)	\
+	findUncompressedJsonbValueByValue((buffer), (flags), (lowbound), (key))
+#define findUncompressedHStoreValue(buffer, flags, lowbound, key, keylen)	\
+	findUncompressedJsonbValue((buffer), (flags), (lowbound), (key), (keylen))
 
-/*
- * 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;
+#define getHStoreValue(buffer, flags, i)			getJsonbValue((buffer), (flags), (i))
+
+typedef ToJsonbState ToHStoreState;
+#define pushHStoreValue(state, r /* WHS_* */, v)	pushJsonbValue((state), (r), (v))
+
+extern uint32 compressHStore(HStoreValue *v, char *buffer);
+
+typedef JsonbIterator HStoreIterator;
+
+#define	HStoreIteratorInit(buffer)					JsonbIteratorInit(buffer)
 
-extern int	hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen);
-extern HStore *hstorePairs(Pairs *pairs, int32 pcount, int32 buflen);
+#define HStoreIteratorGet(it, v, skipNested)	JsonbIteratorGet((it), (v), (skipNested))
 
-extern size_t hstoreCheckKeyLen(size_t len);
-extern size_t hstoreCheckValLen(size_t len);
+text* HStoreValueToText(HStoreValue *v);
 
-extern int	hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen);
-extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs);
+extern HStoreValue* parseHStore(const char *str, int len, bool json);
+
+#define uniqueHStoreValue(v) uniqueJsonbValue(v)
 
 #define HStoreContainsStrategyNumber	7
 #define HStoreExistsStrategyNumber		9
@@ -194,4 +160,18 @@ extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs);
 	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__ */
diff --git a/contrib/hstore/hstore_compat.c b/contrib/hstore/hstore_compat.c
index 6327a8e..0e18505 100644
--- a/contrib/hstore/hstore_compat.c
+++ b/contrib/hstore/hstore_compat.c
@@ -105,9 +105,41 @@ typedef struct
 				pos:31;
 } HOldEntry;
 
-static int	hstoreValidNewFormat(HStore *hs);
-static int	hstoreValidOldFormat(HStore *hs);
+/*
+ * 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))
 
+#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_)]))
+
+/* 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.
@@ -116,7 +148,7 @@ static int	hstoreValidOldFormat(HStore *hs);
  *	2 = exactly valid
  */
 static int
-hstoreValidNewFormat(HStore *hs)
+hstoreValidNewFormat(HStoreV2 *hs)
 {
 	int			count = HS_COUNT(hs);
 	HEntry	   *entries = ARRPTR(hs);
@@ -168,7 +200,7 @@ hstoreValidNewFormat(HStore *hs)
  *	2 = exactly valid
  */
 static int
-hstoreValidOldFormat(HStore *hs)
+hstoreValidOldFormat(HStoreV2 *hs)
 {
 	int			count = hs->size_;
 	HOldEntry  *entries = (HOldEntry *) ARRPTR(hs);
@@ -235,16 +267,26 @@ hstoreValidOldFormat(HStore *hs)
 HStore *
 hstoreUpgrade(Datum orig)
 {
-	HStore	   *hs = (HStore *) PG_DETOAST_DATUM(orig);
+	HStoreV2	   *hs = (HStoreV2 *) PG_DETOAST_DATUM(orig);
 	int			valid_new;
 	int			valid_old;
 	bool		writable;
 
 	/* Return immediately if no conversion needed */
-	if ((hs->size_ & HS_FLAG_NEWVERSION) ||
+	if (VARSIZE_ANY(hs) <= VARHDRSZ ||
+		(hs->size_ & HS_FLAG_NEWVERSION) ||
 		hs->size_ == 0 ||
 		(VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0]))))
-		return hs;
+	{
+		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;
+	}
 
 	valid_new = hstoreValidNewFormat(hs);
 	valid_old = hstoreValidOldFormat(hs);
@@ -266,7 +308,7 @@ hstoreUpgrade(Datum orig)
 				HS_SETCOUNT(hs, HS_COUNT(hs));
 				HS_FIXSIZE(hs, HS_COUNT(hs));
 			}
-			return hs;
+			return (HStore*)hs;
 		}
 		else
 		{
@@ -323,7 +365,7 @@ hstoreUpgrade(Datum orig)
 	 */
 
 	if (!writable)
-		hs = (HStore *) PG_DETOAST_DATUM_COPY(orig);
+		hs = (HStoreV2 *) PG_DETOAST_DATUM_COPY(orig);
 
 	{
 		int			count = hs->size_;
@@ -352,7 +394,7 @@ hstoreUpgrade(Datum orig)
 		HS_FIXSIZE(hs, count);
 	}
 
-	return hs;
+	return (HStore*)hs;
 }
 
 
@@ -361,7 +403,7 @@ Datum		hstore_version_diag(PG_FUNCTION_ARGS);
 Datum
 hstore_version_diag(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = (HStore *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+	HStoreV2	   *hs = (HStoreV2 *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
 	int			valid_new = hstoreValidNewFormat(hs);
 	int			valid_old = hstoreValidOldFormat(hs);
 
diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c
index 2007801..ac77e2e 100644
--- a/contrib/hstore/hstore_gin.c
+++ b/contrib/hstore/hstore_gin.c
@@ -6,21 +6,11 @@
 #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);
 
@@ -41,37 +31,82 @@ 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:
+			cstr = DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric)));
+			item = makeitem(cstr, strlen(cstr), flag);
+			break;
+		case hsvString:
+			item = makeitem(v->string.val, v->string.len, flag);
+			break;
+		default:
+			elog(ERROR, "Wrong hstore 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;
-	HEntry	   *hsent = ARRPTR(hs);
-	char	   *ptr = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			i;
+	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;
 
-	*nentries = 2 * count;
-	if (count)
-		entries = (Datum *) palloc(sizeof(Datum) * 2 * count);
-
-	for (i = 0; i < count; ++i)
+	if (total == 0)
 	{
-		text	   *item;
+		*nentries = 0;
+		PG_RETURN_POINTER(NULL);
+	}
 
-		item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i),
-						KEYFLAG);
-		entries[2 * i] = PointerGetDatum(item);
+	entries = (Datum *) palloc(sizeof(Datum) * total);
 
-		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);
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, false)) != 0)
+	{
+		if (i >= total)
+		{
+			total *= 2;
+			entries = (Datum *) repalloc(entries, sizeof(Datum) * total);
+		}
+
+		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;
+		}
 	}
 
+	*nentries = i;
+
 	PG_RETURN_POINTER(entries);
 }
 
@@ -129,7 +164,8 @@ 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);
 		}
 
@@ -211,3 +247,196 @@ 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:
+			COMP_CRC32(stack->hash_state,
+					   VARDATA_ANY(v->numeric), VARSIZE_ANY_EXHDR(v->numeric));
+			break;
+		case hsvString:
+			COMP_CRC32(stack->hash_state, v->string.val, v->string.len);
+			break;
+		default:
+			elog(ERROR, "Shouldn't take hash of array");
+			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);
+}
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index 9001180..05b3f0a 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -6,8 +6,8 @@
 #include "access/gist.h"
 #include "access/skey.h"
 #include "catalog/pg_type.h"
+#include "utils/pg_crc.h"
 
-#include "crc32.h"
 #include "hstore.h"
 
 /* bigint defines */
@@ -105,6 +105,66 @@ 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:
+			COMP_CRC32(crc, VARDATA_ANY(v->numeric), VARSIZE_ANY_EXHDR(v->numeric));
+			break;
+		default:
+			elog(PANIC, "impossible value %d", v->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)
 {
@@ -113,25 +173,26 @@ ghstore_compress(PG_FUNCTION_ARGS)
 
 	if (entry->leafkey)
 	{
-		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;
+		GISTTYPE   		*res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+		HStore	   		*val = DatumGetHStoreP(entry->key);
 
 		SET_VARSIZE(res, CALCGTSIZE(0));
 
-		for (i = 0; i < count; ++i)
+		if (!HS_ISEMPTY(val))
 		{
-			int			h;
+			int				r;
+			HStoreIterator	*it = HStoreIteratorInit(VARDATA(val));
+			HStoreValue		v;
 
-			h = crc32_sz((char *) HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i));
-			HASH(GETSIGN(res), h);
-			if (!HS_VALISNULL(hsent, i))
+			while((r = HStoreIteratorGet(&it, &v, false)) != 0)
 			{
-				h = crc32_sz((char *) HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i));
-				HASH(GETSIGN(res), h);
+				if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) &&
+					v.type != hsvNull)
+				{
+					int   h = crc32_HStoreValue(&v, r);
+
+					HASH(GETSIGN(res), h);
+				}
 			}
 		}
 
@@ -396,7 +457,8 @@ 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)))
@@ -410,7 +472,8 @@ 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);
@@ -490,7 +553,6 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	PG_RETURN_POINTER(v);
 }
 
-
 Datum
 ghstore_consistent(PG_FUNCTION_ARGS)
 {
@@ -513,82 +575,123 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 	if (strategy == HStoreContainsStrategyNumber ||
 		strategy == HStoreOldContainsStrategyNumber)
 	{
-		HStore	   *query = PG_GETARG_HS(1);
-		HEntry	   *qe = ARRPTR(query);
-		char	   *qv = STRPTR(query);
-		int			count = HS_COUNT(query);
+		BITVECP		qe;
 		int			i;
 
-		for (i = 0; res && i < count; ++i)
+		qe = fcinfo->flinfo->fn_extra;
+		if (qe == NULL)
 		{
-			int			crc = crc32_sz((char *) HS_KEY(qe, qv, i), HS_KEYLEN(qe, i));
+			HStore	   		*query = PG_GETARG_HS(1);
+
+			qe = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(BITVEC));
+			memset(qe, 0, sizeof(BITVEC));
 
-			if (GETBIT(sign, HASHVAL(crc)))
+			if (!HS_ISEMPTY(query))
 			{
-				if (!HS_VALISNULL(qe, i))
+				int				r;
+				HStoreIterator	*it = HStoreIteratorInit(VARDATA(query));
+				HStoreValue		v;
+
+				while((r = HStoreIteratorGet(&it, &v, false)) != 0)
 				{
-					crc = crc32_sz((char *) HS_VAL(qe, qv, i), HS_VALLEN(qe, i));
-					if (!GETBIT(sign, HASHVAL(crc)))
-						res = false;
+					if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) && v.type != hsvNull)
+					{
+						int   crc = crc32_HStoreValue(&v, r);
+
+						HASH(qe, crc);
+					}
 				}
 			}
-			else
+
+			fcinfo->flinfo->fn_extra = qe;
+		}
+
+		LOOPBYTE
+		{
+			if ((sign[i] & qe[i]) != qe[i])
+			{
 				res = false;
+				break;
+			}
 		}
 	}
 	else if (strategy == HStoreExistsStrategyNumber)
 	{
-		text	   *query = PG_GETARG_TEXT_PP(1);
-		int			crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
+		int 	*qval;
 
-		res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
-	}
-	else if (strategy == HStoreExistsAllStrategyNumber)
-	{
-		ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
-		Datum	   *key_datums;
-		bool	   *key_nulls;
-		int			key_count;
-		int			i;
-
-		deconstruct_array(query,
-						  TEXTOID, -1, false, 'i',
-						  &key_datums, &key_nulls, &key_count);
-
-		for (i = 0; res && i < key_count; ++i)
+		qval = fcinfo->flinfo->fn_extra;
+		if (qval == NULL)
 		{
-			int			crc;
+			text	   *query = PG_GETARG_TEXT_PP(1);
+			int			crc = crc32_Key(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
 
-			if (key_nulls[i])
-				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (!(GETBIT(sign, HASHVAL(crc))))
-				res = FALSE;
+			qval = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(*qval));
+			*qval = HASHVAL(crc);
+
+			fcinfo->flinfo->fn_extra = qval;
 		}
+
+		res = (GETBIT(sign, *qval)) ? true : false;
 	}
-	else if (strategy == HStoreExistsAnyStrategyNumber)
+	else if (strategy == HStoreExistsAllStrategyNumber ||
+			 strategy == HStoreExistsAnyStrategyNumber)
 	{
-		ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
-		Datum	   *key_datums;
-		bool	   *key_nulls;
-		int			key_count;
-		int			i;
+		BITVECP	arrentry;
+		int		i;
 
-		deconstruct_array(query,
-						  TEXTOID, -1, false, 'i',
-						  &key_datums, &key_nulls, &key_count);
+		arrentry = fcinfo->flinfo->fn_extra;
+		if (arrentry == NULL)
+		{
+			ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
+			Datum	   *key_datums;
+			bool	   *key_nulls;
+			int			key_count;
 
-		res = FALSE;
+			arrentry = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+										  sizeof(BITVEC));
+			memset(arrentry, 0, sizeof(BITVEC));
 
-		for (i = 0; !res && i < key_count; ++i)
+			deconstruct_array(query,
+							  TEXTOID, -1, false, 'i',
+							  &key_datums, &key_nulls, &key_count);
+
+			for (i = 0; i < key_count; ++i)
+			{
+				int			crc;
+
+				if (key_nulls[i])
+					continue;
+				crc = crc32_Key(VARDATA(key_datums[i]),
+								VARSIZE(key_datums[i]) - VARHDRSZ);
+				HASH(arrentry, crc);
+			}
+
+			fcinfo->flinfo->fn_extra = arrentry;
+		}
+
+		if (strategy == HStoreExistsAllStrategyNumber)
 		{
-			int			crc;
+			LOOPBYTE
+			{
+				if ((sign[i] & arrentry[i]) != arrentry[i])
+				{
+					res = false;
+					break;
+				}
+			}
+		}
+		else /* HStoreExistsAnyStrategyNumber */
+		{
+			res = false;
 
-			if (key_nulls[i])
-				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (GETBIT(sign, HASHVAL(crc)))
-				res = TRUE;
+			LOOPBYTE
+			{
+				if (sign[i] & arrentry[i])
+				{
+					res = true;
+					break;
+				}
+			}
 		}
 	}
 	else
diff --git a/contrib/hstore/hstore_gram.y b/contrib/hstore/hstore_gram.y
new file mode 100644
index 0000000..d0883fa
--- /dev/null
+++ b/contrib/hstore/hstore_gram.y
@@ -0,0 +1,341 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 973a126..0f6887c 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -7,12 +7,15 @@
 
 #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 "utils/builtins.h"
+#include "utils/fmgroids.h"
 #include "utils/json.h"
+#include "utils/guc.h"
 #include "utils/lsyscache.h"
+#include "utils/syscache.h"
 #include "utils/typcache.h"
 
 #include "hstore.h"
@@ -22,507 +25,453 @@ 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))
 
-typedef struct
+static void recvHStore(StringInfo buf, HStoreValue *v, uint32 level,
+					   uint32 header);
+
+typedef enum HStoreOutputKind {
+	JsonOutput = 0x01,
+	LooseOutput = 0x02,
+	ArrayCurlyBraces = 0x04,
+	RootHashDecorated = 0x08,
+	PrettyPrint = 0x10
+} HStoreOutputKind;
+
+static char* HStoreToCString(StringInfo out, char *in,
+							 int len /* just estimation */, HStoreOutputKind kind);
+
+static size_t
+hstoreCheckKeyLen(size_t len)
 {
-	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
+	if (len > HSTORE_MAX_KEY_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for hstore key")));
+	return len;
+}
 
-static bool
-get_val(HSParser *state, bool ignoreeq, bool *escaped)
+static size_t
+hstoreCheckValLen(size_t len)
 {
-	int			st = GV_WAITVAL;
+	if (len > HSTORE_MAX_VALUE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for hstore value")));
+	return len;
+}
 
-	state->wordlen = 32;
-	state->cur = state->word = palloc(state->wordlen);
-	*escaped = false;
 
-	while (1)
+static HStore*
+hstoreDump(HStoreValue *p)
+{
+	uint32			buflen;
+	HStore	 	   *out;
+
+	if (p == NULL)
 	{
-		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__);
+		buflen = 0;
+		out = palloc(VARHDRSZ);
+	}
+	else
+	{
+		buflen = VARHDRSZ + p->size;
+		out = palloc(buflen);
+		SET_VARSIZE(out, buflen);
 
-		state->ptr++;
+		buflen = compressHStore(p, VARDATA(out));
 	}
+	SET_VARSIZE(out, buflen + VARHDRSZ);
+
+	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)));
 }
 
-#define WKEY	0
-#define WVAL	1
-#define WEQ 2
-#define WGT 3
-#define WDEL	4
+static void
+recvHStoreValue(StringInfo buf, HStoreValue *v, uint32 level, int c)
+{
+	uint32  hentry = c & HENTRY_TYPEMASK;
 
+	if (c == -1 /* compatibility */ || hentry == HENTRY_ISNULL)
+	{
+		v->type = hsvNull;
+		v->size = sizeof(HEntry);
+	}
+	else if (hentry == HENTRY_ISHASH || hentry == HENTRY_ISARRAY ||
+			 hentry == HENTRY_ISCALAR)
+	{
+		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, &c);
+		v->string.len = hstoreCheckKeyLen(c);
+		v->size = sizeof(HEntry) + v->string.len;
+	}
+	else
+	{
+		elog(ERROR, "bogus input");
+	}
+}
 
 static void
-parse_hstore(HSParser *state)
+recvHStore(StringInfo buf, HStoreValue *v, uint32 level, uint32 header)
 {
-	int			st = WKEY;
-	bool		escaped = false;
+	uint32	hentry;
+	uint32	i;
+
+	hentry = header & HENTRY_TYPEMASK;
 
-	state->plen = 16;
-	state->pairs = (Pairs *) palloc(sizeof(Pairs) * state->plen);
-	state->pcur = 0;
-	state->ptr = state->begin;
-	state->word = NULL;
+	if (level == 0 && hentry == 0)
+		hentry = HENTRY_ISHASH; /* old version */
 
-	while (1)
+	v->size = 3 * sizeof(HEntry);
+	if (hentry == HENTRY_ISHASH)
 	{
-		if (st == WKEY)
+		v->type = hsvHash;
+		v->hash.npairs = header & HS_COUNT_MASK;
+		if (v->hash.npairs > 0)
 		{
-			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)
+			v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+			for(i=0; i<v->hash.npairs; i++)
 			{
-				state->word[4] = '\0';
-				if (0 == pg_strcasecmp(state->word, "null"))
-					state->pairs[state->pcur].isnull = true;
+				recvHStoreValue(buf, &v->hash.pairs[i].key, level,
+								pq_getmsgint(buf, 4));
+				if (v->hash.pairs[i].key.type != hsvString)
+					elog(ERROR, "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;
 			}
-			state->word = NULL;
-			state->pcur++;
-			st = WDEL;
+
+			uniqueHStoreValue(v);
 		}
-		else if (st == WDEL)
+	}
+	else if (hentry == HENTRY_ISARRAY || hentry == HENTRY_ISCALAR)
+	{
+		v->type = hsvArray;
+		v->array.nelems = header & HS_COUNT_MASK;
+		v->array.scalar = (hentry == HENTRY_ISCALAR) ? true : false;
+
+		if (v->array.scalar && v->array.nelems != 1)
+			elog(ERROR, "bogus input");
+
+		if (v->array.nelems > 0)
 		{
-			if (*(state->ptr) == ',')
-			{
-				st = WKEY;
-			}
-			else if (*(state->ptr) == '\0')
-			{
-				return;
-			}
-			else if (!isspace((unsigned char) *(state->ptr)))
+			v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+			for(i=0; i<v->array.nelems; i++)
 			{
-				elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
+				recvHStoreValue(buf, v->array.elems + i, level,
+								pq_getmsgint(buf, 4));
+				v->size += v->array.elems[i].size;
 			}
 		}
-		else
-			elog(ERROR, "Unknown state %d at line %d in file '%s'", st, __LINE__, __FILE__);
-
-		state->ptr++;
+	}
+	else
+	{
+			elog(ERROR, "bogus input");
 	}
 }
 
-static int
-comparePairs(const void *a, const void *b)
+PG_FUNCTION_INFO_V1(hstore_recv);
+Datum		hstore_recv(PG_FUNCTION_ARGS);
+Datum
+hstore_recv(PG_FUNCTION_ARGS)
 {
-	const Pairs *pa = a;
-	const Pairs *pb = b;
-
-	if (pa->keylen == pb->keylen)
-	{
-		int			res = memcmp(pa->key, pb->key, pa->keylen);
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	HStoreValue	v;
 
-		if (res)
-			return res;
+	recvHStore(buf, &v, 0, pq_getmsgint(buf, 4));
 
-		/* 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;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-/*
- * 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)
+PG_FUNCTION_INFO_V1(hstore_from_text);
+Datum		hstore_from_text(PG_FUNCTION_ARGS);
+Datum
+hstore_from_text(PG_FUNCTION_ARGS)
 {
-	Pairs	   *ptr,
-			   *res;
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	*buflen = 0;
-	if (l < 2)
+	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))
 	{
-		if (l == 1)
-			*buflen = a->keylen + ((a->isnull) ? 0 : a->vallen);
-		return l;
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
 	}
-
-	qsort((void *) a, l, sizeof(Pairs), comparePairs);
-	ptr = a + 1;
-	res = a;
-	while (ptr - a < l)
+	else
 	{
-		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));
-		}
+		text	   	*val = NULL;
 
-		ptr++;
+		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);
 	}
 
-	*buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
-	return res + 1 - a;
-}
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-size_t
-hstoreCheckKeyLen(size_t len)
-{
-	if (len > HSTORE_MAX_KEY_LEN)
-		ereport(ERROR,
-				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
-				 errmsg("string too long for hstore key")));
-	return len;
-}
-
-size_t
-hstoreCheckValLen(size_t len)
-{
-	if (len > HSTORE_MAX_VALUE_LEN)
-		ereport(ERROR,
-				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
-				 errmsg("string too long for hstore value")));
-	return len;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-HStore *
-hstorePairs(Pairs *pairs, int32 pcount, int32 buflen)
+PG_FUNCTION_INFO_V1(hstore_from_bool);
+Datum		hstore_from_bool(PG_FUNCTION_ARGS);
+Datum
+hstore_from_bool(PG_FUNCTION_ARGS)
 {
-	HStore	   *out;
-	HEntry	   *entry;
-	char	   *ptr;
-	char	   *buf;
-	int32		len;
-	int32		i;
-
-	len = CALCDATASIZE(pcount, buflen);
-	out = palloc(len);
-	SET_VARSIZE(out, len);
-	HS_SETCOUNT(out, pcount);
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	if (pcount == 0)
-		return out;
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	entry = ARRPTR(out);
-	buf = ptr = STRPTR(out);
+	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);
 
-	for (i = 0; i < pcount; i++)
-		HS_ADDITEM(entry, buf, ptr, pairs[i]);
+	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);
+	}
 
-	HS_FINALIZE(out, pcount, buf, ptr);
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-	return out;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_in);
-Datum		hstore_in(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_from_numeric);
+Datum		hstore_from_numeric(PG_FUNCTION_ARGS);
 Datum
-hstore_in(PG_FUNCTION_ARGS)
+hstore_from_numeric(PG_FUNCTION_ARGS)
 {
-	HSParser	state;
-	int32		buflen;
-	HStore	   *out;
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	state.begin = PG_GETARG_CSTRING(0);
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	parse_hstore(&state);
+	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);
 
-	state.pcur = hstoreUniquePairs(state.pairs, state.pcur, &buflen);
+	if (PG_ARGISNULL(1))
+	{
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
+	}
+	else
+	{
+		pair.value.type = hsvNumeric;
+		pair.value.numeric = PG_GETARG_NUMERIC(1);
+		pair.value.size = sizeof(HEntry) + sizeof(HEntry) +
+							VARSIZE_ANY(pair.value.numeric);
+	}
 
-	out = hstorePairs(state.pairs, state.pcur, buflen);
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_recv);
-Datum		hstore_recv(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_from_th);
+Datum		hstore_from_th(PG_FUNCTION_ARGS);
 Datum
-hstore_recv(PG_FUNCTION_ARGS)
+hstore_from_th(PG_FUNCTION_ARGS)
 {
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
-	int32		i;
-	int32		pcount;
-	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	pcount = pq_getmsgint(buf, 4);
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	if (pcount == 0)
+	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))
 	{
-		out = hstorePairs(NULL, 0, 0);
-		PG_RETURN_POINTER(out);
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
 	}
+	else
+	{
+		HStore	   	*val = NULL;
 
-	pairs = palloc(pcount * sizeof(Pairs));
+		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;
+	}
 
-	for (i = 0; i < pcount; ++i)
-	{
-		int			rawlen = pq_getmsgint(buf, 4);
-		int			len;
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-		if (rawlen < 0)
-			ereport(ERROR,
-					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-					 errmsg("null value not allowed for hstore key")));
+	PG_RETURN_POINTER(hstoreDump(&v));
+}
 
-		pairs[i].key = pq_getmsgtext(buf, rawlen, &len);
-		pairs[i].keylen = hstoreCheckKeyLen(len);
-		pairs[i].needfree = true;
+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;
 
-		rawlen = pq_getmsgint(buf, 4);
-		if (rawlen < 0)
-		{
-			pairs[i].val = NULL;
-			pairs[i].vallen = 0;
-			pairs[i].isnull = true;
-		}
-		else
-		{
-			pairs[i].val = pq_getmsgtext(buf, rawlen, &len);
-			pairs[i].vallen = hstoreCheckValLen(len);
-			pairs[i].isnull = false;
-		}
+	if (PG_ARGISNULL(0))
+	{
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
 	}
+	else
+	{
+		text	*scalar;
 
-	pcount = hstoreUniquePairs(pairs, pcount, &buflen);
+		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);
+	}
 
-	out = hstorePairs(pairs, pcount, buflen);
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&a));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_from_text);
-Datum		hstore_from_text(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_scalar_from_bool);
+Datum		hstore_scalar_from_bool(PG_FUNCTION_ARGS);
 Datum
-hstore_from_text(PG_FUNCTION_ARGS)
+hstore_scalar_from_bool(PG_FUNCTION_ARGS)
 {
-	text	   *key;
-	text	   *val = NULL;
-	Pairs		p;
-	HStore	   *out;
+	HStoreValue	a, v;
 
 	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
+	{
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
+	}
+	else
+	{
+		v.type = hsvBool;
+		v.boolean = PG_GETARG_BOOL(0);
+		v.size = sizeof(HEntry);
+	}
 
-	p.needfree = false;
-	key = PG_GETARG_TEXT_PP(0);
-	p.key = VARDATA_ANY(key);
-	p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	if (PG_ARGISNULL(1))
+	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))
 	{
-		p.vallen = 0;
-		p.isnull = true;
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
 	}
 	else
 	{
-		val = PG_GETARG_TEXT_PP(1);
-		p.val = VARDATA_ANY(val);
-		p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
-		p.isnull = false;
+		v.type = hsvNumeric;
+		v.numeric = PG_GETARG_NUMERIC(0);
+		v.size = VARSIZE_ANY(v.numeric) + 2*sizeof(HEntry);
 	}
 
-	out = hstorePairs(&p, 1, p.keylen + p.vallen);
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&a));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_from_arrays);
 Datum		hstore_from_arrays(PG_FUNCTION_ARGS);
 Datum
 hstore_from_arrays(PG_FUNCTION_ARGS)
 {
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
+	HStoreValue v;
 	Datum	   *key_datums;
 	bool	   *key_nulls;
 	int			key_count;
@@ -589,7 +538,10 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
 		Assert(key_count == value_count);
 	}
 
-	pairs = palloc(key_count * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2 * sizeof(HEntry);
+	v.hash.pairs = palloc(key_count * sizeof(*v.hash.pairs));
+	v.hash.npairs = key_count;
 
 	for (i = 0; i < key_count; ++i)
 	{
@@ -598,31 +550,34 @@ 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])
 		{
-			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;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
 		else
 		{
-			pairs[i].key = VARDATA_ANY(key_datums[i]);
-			pairs[i].val = VARDATA_ANY(value_datums[i]);
-			pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
-			pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i]));
-			pairs[i].isnull = false;
-			pairs[i].needfree = false;
+			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;
 		}
+
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
 	}
 
-	key_count = hstoreUniquePairs(pairs, key_count, &buflen);
+	uniqueHStoreValue(&v);
 
-	out = hstorePairs(pairs, key_count, buflen);
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
 
@@ -634,9 +589,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
 	ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
 	int			ndims = ARR_NDIM(in_array);
 	int			count;
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
+	HStoreValue	v;
 	Datum	   *in_datums;
 	bool	   *in_nulls;
 	int			in_count;
@@ -647,8 +600,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
 	switch (ndims)
 	{
 		case 0:
-			out = hstorePairs(NULL, 0, 0);
-			PG_RETURN_POINTER(out);
+			PG_RETURN_POINTER(hstoreDump(NULL));
 
 		case 1:
 			if ((ARR_DIMS(in_array)[0]) % 2)
@@ -676,7 +628,10 @@ hstore_from_array(PG_FUNCTION_ARGS)
 
 	count = in_count / 2;
 
-	pairs = palloc(count * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2*sizeof(HEntry);
+	v.hash.npairs = count;
+	v.hash.pairs = palloc(count * sizeof(HStorePair));
 
 	for (i = 0; i < count; ++i)
 	{
@@ -685,31 +640,33 @@ 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])
 		{
-			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;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
 		else
 		{
-			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.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;
 		}
-	}
 
-	count = hstoreUniquePairs(pairs, count, &buflen);
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
+	}
 
-	out = hstorePairs(pairs, count, buflen);
+	uniqueHStoreValue(&v);
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
 /* most of hstore_from_record is shamelessly swiped from record_out */
@@ -739,19 +696,17 @@ Datum
 hstore_from_record(PG_FUNCTION_ARGS)
 {
 	HeapTupleHeader rec;
-	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;
+	HStore		   *out;
+	HStoreValue	   v;
+	Oid				tupType;
+	int32			tupTypmod;
+	TupleDesc		tupdesc;
+	HeapTupleData 	tuple;
+	RecordIOData   *my_extra;
+	int				ncolumns;
+	int				i;
+	Datum	   	   *values;
+	bool	   	   *nulls;
 
 	if (PG_ARGISNULL(0))
 	{
@@ -807,7 +762,10 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	pairs = palloc(ncolumns * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2*sizeof(HEntry);
+	v.hash.npairs = ncolumns;
+	v.hash.pairs = palloc(ncolumns * sizeof(HStorePair));
 
 	if (rec)
 	{
@@ -829,7 +787,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		nulls = NULL;
 	}
 
-	for (i = 0, j = 0; i < ncolumns; ++i)
+	for (i = 0; i < ncolumns; ++i)
 	{
 		ColumnIOData *column_info = &my_extra->columns[i];
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
@@ -839,46 +797,65 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		if (tupdesc->attrs[i]->attisdropped)
 			continue;
 
-		pairs[j].key = NameStr(tupdesc->attrs[i]->attname);
-		pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(tupdesc->attrs[i]->attname)));
+		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;
 
 		if (!nulls || nulls[i])
 		{
-			pairs[j].val = NULL;
-			pairs[j].vallen = 4;
-			pairs[j].isnull = true;
-			pairs[j].needfree = false;
-			++j;
-			continue;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
-
-		/*
-		 * Convert the column value to text
-		 */
-		if (column_info->column_type != column_type)
+		else
 		{
-			bool		typIsVarlena;
+			/*
+			 * 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 (column_type == NUMERICOID)
+			{	/* XXX float... int... */
+				v.hash.pairs[i].value.type = hsvNumeric;
+				v.hash.pairs[i].value.numeric = DatumGetNumeric(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;
+				}
 
-			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]);
 
-		value = OutputFunctionCall(&column_info->proc, values[i]);
+				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;
+			}
+		}
 
-		pairs[j].val = value;
-		pairs[j].vallen = hstoreCheckValLen(strlen(value));
-		pairs[j].isnull = false;
-		pairs[j].needfree = false;
-		++j;
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
 	}
 
-	ncolumns = hstoreUniquePairs(pairs, j, &buflen);
+	uniqueHStoreValue(&v);
 
-	out = hstorePairs(pairs, ncolumns, buflen);
+	out = hstoreDump(&v);
 
 	ReleaseTupleDesc(tupdesc);
 
@@ -893,8 +870,6 @@ 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;
@@ -940,8 +915,6 @@ 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
@@ -949,7 +922,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 	 * domain nulls.
 	 */
 
-	if (HS_COUNT(hs) == 0 && rec)
+	if (HS_ISEMPTY(hs) && rec)
 		PG_RETURN_POINTER(rec);
 
 	tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
@@ -1013,9 +986,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 	{
 		ColumnIOData *column_info = &my_extra->columns[i];
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
-		char	   *value;
-		int			idx;
-		int			vallen;
+		HStoreValue	*v = NULL;
 
 		/* Ignore dropped columns in datatype */
 		if (tupdesc->attrs[i]->attisdropped)
@@ -1024,9 +995,12 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 			continue;
 		}
 
-		idx = hstoreFindKey(hs, 0,
-							NameStr(tupdesc->attrs[i]->attname),
-							strlen(NameStr(tupdesc->attrs[i]->attname)));
+		if (!HS_ISEMPTY(hs))
+		{
+			char *key = NameStr(tupdesc->attrs[i]->attname);
+
+			v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HASH, NULL, key, strlen(key));
+		}
 
 		/*
 		 * we can't just skip here if the key wasn't found since we might have
@@ -1036,7 +1010,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 (idx < 0 && rec)
+		if (v == NULL && rec)
 			continue;
 
 		/*
@@ -1052,7 +1026,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 			column_info->column_type = column_type;
 		}
 
-		if (idx < 0 || HS_VALISNULL(entries, idx))
+		if (v == NULL || v->type == hsvNull)
 		{
 			/*
 			 * need InputFunctionCall to happen even for nulls, so that domain
@@ -1065,12 +1039,29 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 		}
 		else
 		{
-			vallen = HS_VALLEN(entries, idx);
-			value = palloc(1 + vallen);
-			memcpy(value, HS_VAL(entries, ptr, idx), vallen);
-			value[vallen] = 0;
+			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(PANIC, "Wrong hstore");
 
-			values[i] = InputFunctionCall(&column_info->proc, value,
+			values[i] = InputFunctionCall(&column_info->proc, s,
 										  column_info->typioparam,
 										  tupdesc->attrs[i]->atttypmod);
 			nulls[i] = false;
@@ -1084,125 +1075,377 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(rettuple));
 }
 
+static void
+printIndent(StringInfo out, bool isRootHash, HStoreOutputKind kind, int level)
+{
+	if (kind & PrettyPrint)
+	{
+		int i;
+
+		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 char *
-cpw(char *dst, char *src, int len)
+static void
+escape_hstore(StringInfo out, char *string, uint32 len)
 {
-	char	   *ptr = src;
+	char       *ptr = string;
 
-	while (ptr - src < len)
+	appendStringInfoCharMacro(out, '"');
+	while (ptr - string < len)
 	{
 		if (*ptr == '"' || *ptr == '\\')
-			*dst++ = '\\';
-		*dst++ = *ptr++;
+			appendStringInfoCharMacro(out, '\\');
+		appendStringInfoCharMacro(out, *ptr);
+		ptr++;
 	}
-	return dst;
+	appendStringInfoCharMacro(out, '"');
 }
 
-PG_FUNCTION_INFO_V1(hstore_out);
-Datum		hstore_out(PG_FUNCTION_ARGS);
-Datum
-hstore_out(PG_FUNCTION_ARGS)
+static void
+putEscapedString(StringInfo out, HStoreOutputKind kind,
+				 char *string, uint32 len)
 {
-	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);
+	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 && JsonbStringIsNumber(string, len))
+			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);
+	}
+}
+
+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(PANIC, "Unknown type");
+	}
+}
+
+static bool
+needBrackets(int level, bool isArray, HStoreOutputKind kind, bool isScalar)
+{
+	bool res;
 
-	if (count == 0)
-		PG_RETURN_CSTRING(pstrdup(""));
+	if (isArray && isScalar)
+		res = false;
+	else if (level == 0)
+		res = (isArray || (kind & RootHashDecorated)) ? true : false;
+	else
+		res = true;
 
-	buflen = 0;
+	return res;
+}
 
-	/*
-	 * 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.
-	 */
+static bool
+isArrayBrackets(HStoreOutputKind kind)
+{
+	return ((kind & ArrayCurlyBraces) == 0) ? true : false;
+}
+		
+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;
 
-	for (i = 0; i < count; i++)
+	if (out == NULL)
+		out = makeStringInfo();
+
+	if (in == NULL)
 	{
-		/* 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));
+		appendStringInfoString(out, "");
+		return out->data;
 	}
 
-	out = ptr = palloc(buflen);
+	enlargeStringInfo(out, (len >= 0) ? len : 64);
+
+	it = HStoreIteratorInit(in);
 
-	for (i = 0; i < count; i++)
+	while((type = HStoreIteratorGet(&it, &v, false)) != 0)
 	{
-		*ptr++ = '"';
-		ptr = cpw(ptr, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		*ptr++ = '"';
-		*ptr++ = '=';
-		*ptr++ = '>';
-		if (HS_VALISNULL(entries, i))
-		{
-			*ptr++ = 'N';
-			*ptr++ = 'U';
-			*ptr++ = 'L';
-			*ptr++ = 'L';
-		}
-		else
+reout:
+		switch(type)
 		{
-			*ptr++ = '"';
-			ptr = cpw(ptr, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
-			*ptr++ = '"';
-		}
+			case WHS_BEGIN_ARRAY:
+				if (first == false)
+				{
+					appendBinaryStringInfo(out, ", ", 2);
+					printCR(out, kind);
+				}
+				first = true;
 
-		if (i + 1 != count)
-		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
+				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(PANIC, "Wrong flags");
 		}
 	}
-	*ptr = '\0';
 
-	PG_RETURN_CSTRING(out);
+	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);
+	}
+
+	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));
+
+	PG_RETURN_CSTRING(out);
+}
 
 PG_FUNCTION_INFO_V1(hstore_send);
 Datum		hstore_send(PG_FUNCTION_ARGS);
 Datum
 hstore_send(PG_FUNCTION_ARGS)
 {
-	HStore	   *in = PG_GETARG_HS(0);
-	int			i;
-	int			count = HS_COUNT(in);
-	char	   *base = STRPTR(in);
-	HEntry	   *entries = ARRPTR(in);
-	StringInfoData buf;
+	HStore	   		*in = PG_GETARG_HS(0);
+	StringInfoData	buf;
 
 	pq_begintypsend(&buf);
 
-	pq_sendint(&buf, count, 4);
-
-	for (i = 0; i < count; i++)
+	if (HS_ISEMPTY(in))
+	{
+		pq_sendint(&buf, 0, 4);
+	}
+	else
 	{
-		int32		keylen = HS_KEYLEN(entries, i);
+		HStoreIterator	*it;
+		int				type;
+		HStoreValue		v;
+		uint32			flag;
+		bytea			*nbuf;
 
-		pq_sendint(&buf, keylen, 4);
-		pq_sendtext(&buf, HS_KEY(entries, base, i), keylen);
-		if (HS_VALISNULL(entries, i))
-		{
-			pq_sendint(&buf, -1, 4);
-		}
-		else
-		{
-			int32		vallen = HS_VALLEN(entries, i);
+		enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */);
 
-			pq_sendint(&buf, vallen, 4);
-			pq_sendtext(&buf, HS_VAL(entries, base, i), vallen);
+		it = HStoreIteratorInit(VARDATA_ANY(in));
+
+		while((type = HStoreIteratorGet(&it, &v, false)) != 0)
+		{
+			switch(type)
+			{
+				case WHS_BEGIN_ARRAY:
+					flag = (v.array.scalar) ? HENTRY_ISCALAR : 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, VARSIZE_ANY(nbuf) | HENTRY_ISNUMERIC, 4);
+							pq_sendbytes(&buf, (char*)nbuf, VARSIZE_ANY(nbuf));
+							break;
+						default:
+							elog(PANIC, "Wrong type: %u", v.type);
+					}
+					break;
+				case WHS_END_ARRAY:
+				case WHS_END_HASH:
+					break;
+				default:
+					elog(PANIC, "Wrong flags");
+			}
 		}
 	}
 
@@ -1224,124 +1467,28 @@ Datum
 hstore_to_json_loose(PG_FUNCTION_ARGS)
 {
 	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);
-	bool		is_number;
-	StringInfo	src,
-				dst;
-
-	if (count == 0)
-		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
-
-	buflen = 3;
+	text	   *out;
 
-	/*
-	 * Formula adjusted slightly from the logic in hstore_out. We have to take
-	 * account of out treatment of booleans to be a bit more pessimistic about
-	 * the length of values.
-	 */
+	if (HS_ISEMPTY(in))
+	{
+		out = cstring_to_text_with_len("{}",2);
+	}
+	else
+	{
+		StringInfo	str;
 
-	for (i = 0; i < count; i++)
-	{
-		/* include "" and colon-space and comma-space */
-		buflen += 6 + 2 * HS_KEYLEN(entries, i);
-		/* include "" only if nonnull */
-		buflen += 3 + (HS_VALISNULL(entries, i)
-					   ? 1
-					   : 2 * HS_VALLEN(entries, i));
-	}
-
-	out = ptr = palloc(buflen);
-
-	src = makeStringInfo();
-	dst = makeStringInfo();
-
-	*ptr++ = '{';
-
-	for (i = 0; i < count; i++)
-	{
-		resetStringInfo(src);
-		resetStringInfo(dst);
-		appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		escape_json(dst, src->data);
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
-		*ptr++ = ':';
-		*ptr++ = ' ';
-		resetStringInfo(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(src);
-			appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
+		str = makeStringInfo();
+		appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
 
-			/*
-			 * don't treat something with a leading zero followed by another
-			 * digit as numeric - could be a zip code or similar
-			 */
-			if (src->len > 0 &&
-				!(src->data[0] == '0' &&
-				  isdigit((unsigned char) src->data[1])) &&
-				strspn(src->data, "+-0123456789Ee.") == src->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(src->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;
+		HStoreToCString(str, VARDATA_ANY(in), VARSIZE_ANY(in), 
+						SET_PRETTY_PRINT_VAR(JsonOutput | RootHashDecorated | LooseOutput));
 
-					dval = strtod(src->data, &endptr);
-					(void) dval;
-					if (*endptr == '\0')
-						is_number = true;
-				}
-			}
-			if (is_number)
-				appendBinaryStringInfo(dst, src->data, src->len);
-			else
-				escape_json(dst, src->data);
-		}
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
+		out = (text*)str->data;
 
-		if (i + 1 != count)
-		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
-		}
+		SET_VARSIZE(out, str->len);
 	}
-	*ptr++ = '}';
-	*ptr = '\0';
 
-	PG_RETURN_TEXT_P(cstring_to_text(out));
+	PG_RETURN_TEXT_P(out);
 }
 
 PG_FUNCTION_INFO_V1(hstore_to_json);
@@ -1350,74 +1497,310 @@ Datum
 hstore_to_json(PG_FUNCTION_ARGS)
 {
 	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);
-	StringInfo	src,
-				dst;
+	text	   *out;
 
-	if (count == 0)
-		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
+	if (HS_ISEMPTY(in))
+	{
+		out = cstring_to_text_with_len("{}",2);
+	}
+	else
+	{
+		StringInfo	str;
 
-	buflen = 3;
+		str = makeStringInfo();
+		appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
 
-	/*
-	 * Formula adjusted slightly from the logic in hstore_out. We have to take
-	 * account of out treatment of booleans to be a bit more pessimistic about
-	 * the length of values.
-	 */
+		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;
+	HeapTuple   	tuple;
+
+	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);
+	}
+
+	return funcOid;
+}
 
-	for (i = 0; i < count; i++)
+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;
+	Oid				castOid = InvalidOid;
+	int				valueType = hsvString;
+	FmgrInfo		castInfo;
+	CoercionMethod	method;
+
+	if (ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)) == 0)
+		PG_RETURN_POINTER(hstoreDump(NULL));
+
+	switch(ARR_ELEMTYPE(array))
 	{
-		/* include "" and colon-space and comma-space */
-		buflen += 6 + 2 * HS_KEYLEN(entries, i);
-		/* include "" only if nonnull */
-		buflen += 3 + (HS_VALISNULL(entries, i)
-					   ? 1
-					   : 2 * HS_VALLEN(entries, i));
+		case BOOLOID:
+			valueType = hsvBool;
+			break;
+		case INT2OID:
+		case INT4OID:
+		case INT8OID:
+		case FLOAT4OID:
+		case FLOAT8OID:
+			castOid = searchCast(ARR_ELEMTYPE(array), NUMERICOID, &method);
+			Assert(castOid != InvalidOid);
+		case NUMERICOID:
+			valueType = hsvNumeric;
+			break;
+		default:
+			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");
+		case TEXTOID:
+			valueType = hsvString;
+			break;
 	}
 
-	out = ptr = palloc(buflen);
+	if (castOid != InvalidOid)
+		fmgr_info(castOid, &castInfo);
 
-	src = makeStringInfo();
-	dst = makeStringInfo();
+	iterator = array_create_iterator(array, 0);
 
-	*ptr++ = '{';
+	value.type = hsvArray;
+	value.array.scalar = false;
+	for(i=0; i<ncounters; i++)
+	{
+		value.array.nelems = dims[i];
+		result = pushHStoreValue(&state, WHS_BEGIN_ARRAY, &value);
+	}
 
-	for (i = 0; i < count; i++)
+	while(array_iterate(iterator, &datum, &isnull))
 	{
-		resetStringInfo(src);
-		resetStringInfo(dst);
-		appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		escape_json(dst, src->data);
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
-		*ptr++ = ':';
-		*ptr++ = ' ';
-		resetStringInfo(dst);
-		if (HS_VALISNULL(entries, i))
-			appendStringInfoString(dst, "null");
+		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);
+			}
+		}
 		else
 		{
-			resetStringInfo(src);
-			appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
-			escape_json(dst, src->data);
+			counters[i]++;
 		}
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
 
-		if (i + 1 != count)
+		if (isnull)
+		{
+			value.type = hsvNull;
+			value.size = sizeof(HEntry);
+		}
+		else
 		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
+			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, "Impossible state: %d", valueType);
+			}
 		}
+
+		result = pushHStoreValue(&state, WHS_ELEM, &value);
+	}
+
+	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);
+}
+
+PG_FUNCTION_INFO_V1(hstore2jsonb);
+Datum		hstore2jsonb(PG_FUNCTION_ARGS);
+Datum
+hstore2jsonb(PG_FUNCTION_ARGS)
+{
+	HStore	*hs = PG_GETARG_HS(0);
+	Jsonb	*jb = palloc(VARSIZE_ANY(hs));
+
+	memcpy(jb, hs, VARSIZE_ANY(hs));
+
+	if (VARSIZE_ANY_EXHDR(jb) >= sizeof(uint32))
+	{
+		uint32 *header = (uint32*)VARDATA_ANY(jb);
+
+		*header &= ~JB_FLAG_UNUSED;
+	}
+
+	PG_RETURN_JSONB(jb);
+}
+
+PG_FUNCTION_INFO_V1(jsonb2hstore);
+Datum		jsonb2hstore(PG_FUNCTION_ARGS);
+Datum
+jsonb2hstore(PG_FUNCTION_ARGS)
+{
+	Jsonb	*jb = PG_GETARG_JSONB(0);
+	HStore	*hs = palloc(VARSIZE_ANY(jb));
+
+	memcpy(hs, jb, VARSIZE_ANY(jb));
+
+	if (VARSIZE_ANY_EXHDR(hs) >= sizeof(uint32))
+	{
+		uint32	*header = (uint32*)VARDATA_ANY(hs);
+
+		*header |= HS_FLAG_NEWVERSION;
+	}
+
+	PG_RETURN_POINTER(hs);
+}
+
+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);
+
+	if (l > sizeof(uint32))
+	{
+		uint32	*header = (uint32*)buffer;
+
+		*header |= HS_FLAG_NEWVERSION;
 	}
-	*ptr++ = '}';
-	*ptr = '\0';
 
-	PG_RETURN_TEXT_P(cstring_to_text(out));
+	return l;
 }
+
+
+
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 45edb04..06cc450 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -25,154 +25,597 @@ 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;
 
-/*
- * 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)
+	deconstruct_array(a,
+					  TEXTOID, -1, false, 'i',
+					  &key_datums, &key_nulls, &key_count);
+
+	if (key_count == 0)
+		return NULL;
+
+	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)
 {
-	HEntry	   *entries = ARRPTR(hs);
-	int			stopLow = lowbound ? *lowbound : 0;
-	int			stopHigh = HS_COUNT(hs);
-	int			stopMiddle;
-	char	   *base = STRPTR(hs);
+	HStoreValue		*stopLow = a->array.elems + ((lowbound) ? *lowbound : 0),
+					*stopHigh = a->array.elems + a->array.nelems,
+					*stopMiddle;
 
 	while (stopLow < stopHigh)
 	{
-		int			difference;
+		int diff;
 
 		stopMiddle = stopLow + (stopHigh - stopLow) / 2;
 
-		if (HS_KEYLEN(entries, stopMiddle) == keylen)
-			difference = memcmp(HS_KEY(entries, base, stopMiddle), key, keylen);
+		if (keylen == stopMiddle->string.len)
+			diff = memcmp(stopMiddle->string.val, key, keylen);
 		else
-			difference = (HS_KEYLEN(entries, stopMiddle) > keylen) ? 1 : -1;
+			diff = (stopMiddle->string.len > keylen) ? 1 : -1;
 
-		if (difference == 0)
+		if (diff == 0)
 		{
 			if (lowbound)
-				*lowbound = stopMiddle + 1;
+				*lowbound = (stopMiddle - a->array.elems) + 1;
 			return stopMiddle;
 		}
-		else if (difference < 0)
+		else if (diff < 0)
+		{
 			stopLow = stopMiddle + 1;
+		}
 		else
+		{
 			stopHigh = stopMiddle;
+		}
 	}
 
 	if (lowbound)
-		*lowbound = stopLow;
-	return -1;
+		*lowbound = (stopLow - a->array.elems) + 1;
+
+	return NULL;
 }
 
-Pairs *
-hstoreArrayToPairs(ArrayType *a, int *npairs)
+PG_FUNCTION_INFO_V1(hstore_fetchval);
+Datum		hstore_fetchval(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval(PG_FUNCTION_ARGS)
 {
-	Datum	   *key_datums;
-	bool	   *key_nulls;
-	int			key_count;
-	Pairs	   *key_pairs;
-	int			bufsiz;
-	int			i,
-				j;
+	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);
+}
 
-	deconstruct_array(a,
-					  TEXTOID, -1, false, 'i',
-					  &key_datums, &key_nulls, &key_count);
+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));
 
-	if (key_count == 0)
+		memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+		PG_RETURN_NUMERIC(out);
+	}
+
+	PG_RETURN_NULL();
+}
+
+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)
 	{
-		*npairs = 0;
-		return NULL;
+		Numeric		out = palloc(VARSIZE_ANY(v->numeric));
+
+		memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+		PG_RETURN_NUMERIC(out);
 	}
 
-	key_pairs = palloc(sizeof(Pairs) * key_count);
+	PG_RETURN_NULL();
+}
 
-	for (i = 0, j = 0; i < key_count; i++)
+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)
+{
+	bool	negative = false;
+	char 	*p = c;
+
+	*acc = 0;
+
+	while(isspace(*p) && p - c < l)
+		p++;
+
+	if (p - c >= l)
+		return false;
+
+	if (*p == '-')
 	{
-		if (!key_nulls[i])
+		negative = true;
+		p++;
+	}
+	else if (*p == '+')
+	{
+		p++;
+	}
+
+	if (p - c >= l)
+		return false;
+
+
+	while(p - c < l)
+	{
+		if (!isdigit(*p))
+			return false;
+
+		*acc *= 10;
+		*acc += (*p - '0');
+		p++;
+	}
+
+	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)
+		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;
+
+	if (path_len == 0)
+		return v;
+
+	for(i=0; v != NULL && i<path_len; i++)
+	{
+		uint32	header;
+
+		if (v->type != hsvBinary || path_nulls[i])
+			return NULL;
+
+		header = *(uint32*)v->binary.data;
+
+		if (header & HS_FLAG_HASH)
 		{
-			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++;
+			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
+		{
+			elog(PANIC,"wrong header type: %08x", header);
 		}
 	}
 
-	*npairs = hstoreUniquePairs(key_pairs, j, &bufsiz);
+	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;
 
-	return key_pairs;
+	if ((out = HStoreValueToText(hstoreDeepFetch(hs, path))) == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_TEXT_P(out);
 }
 
+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);
 
-PG_FUNCTION_INFO_V1(hstore_fetchval);
-Datum		hstore_fetchval(PG_FUNCTION_ARGS);
+	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);
 Datum
-hstore_fetchval(PG_FUNCTION_ARGS)
+hstore_fetchval_path_boolean(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	HEntry	   *entries = ARRPTR(hs);
-	text	   *out;
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
+	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			*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);
 
-	if (idx < 0 || HS_VALISNULL(entries, idx))
+		Assert(v->type == hsvBinary);
+		SET_VARSIZE(out, VARHDRSZ + v->binary.len);
+		memcpy(VARDATA(out), v->binary.data, v->binary.len);
+	}
+
+	return out;
+}
+
+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)
 		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;
 
-	out = cstring_to_text_with_len(HS_VAL(entries, STRPTR(hs), idx),
-								   HS_VALLEN(entries, idx));
+	if (!HS_ISEMPTY(hs))
+		v = getHStoreValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, i);
 
-	PG_RETURN_TEXT_P(out);
+	if ((out = HStoreValueToHStore(v)) == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_POINTER(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);
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
+	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_RETURN_BOOL(idx >= 0);
+
+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);
+
+	PG_RETURN_BOOL(hstoreDeepFetch(hs, path) != NULL);
 }
 
 
+
 PG_FUNCTION_INFO_V1(hstore_exists_any);
 Datum		hstore_exists_any(PG_FUNCTION_ARGS);
 Datum
 hstore_exists_any(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(keys, &nkeys);
-	int			i;
-	int			lowbound = 0;
-	bool		res = false;
-
+	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;
 	/*
 	 * we exploit the fact that the pairs list is already sorted into strictly
-	 * increasing order to narrow the hstoreFindKey search; each search can
+	 * 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.
 	 */
-	for (i = 0; i < nkeys; i++)
+	for (i = 0; i < v->array.nelems; i++)
 	{
-		int			idx = hstoreFindKey(hs, &lowbound,
-									  key_pairs[i].key, key_pairs[i].keylen);
-
-		if (idx >= 0)
+		if (findUncompressedHStoreValueByValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, plowbound,
+											   v->array.elems + i) != NULL)
 		{
 			res = true;
 			break;
@@ -188,26 +631,36 @@ Datum		hstore_exists_all(PG_FUNCTION_ARGS);
 Datum
 hstore_exists_all(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(keys, &nkeys);
-	int			i;
-	int			lowbound = 0;
-	bool		res = true;
+	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);
+	}
 
+	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 hstoreFindKey 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 findUncompressedHStoreValue search;
+	 * each search can start one entry past the previous "found" entry,
+	 * or at the lower bound of the last search.
 	 */
-	for (i = 0; i < nkeys; i++)
+	for (i = 0; i < v->array.nelems; i++)
 	{
-		int			idx = hstoreFindKey(hs, &lowbound,
-									  key_pairs[i].key, key_pairs[i].keylen);
-
-		if (idx < 0)
+		if (findUncompressedHStoreValueByValue(VARDATA(hs),
+											   HS_FLAG_HASH | HS_FLAG_ARRAY,
+											   plowbound,
+											   v->array.elems + i) == NULL)
 		{
 			res = false;
 			break;
@@ -223,14 +676,18 @@ Datum		hstore_defined(PG_FUNCTION_ARGS);
 Datum
 hstore_defined(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	HEntry	   *entries = ARRPTR(hs);
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
-	bool		res = (idx >= 0 && !HS_VALISNULL(entries, idx));
-
-	PG_RETURN_BOOL(res);
+	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));
 }
 
 
@@ -239,483 +696,1336 @@ Datum		hstore_delete(PG_FUNCTION_ARGS);
 Datum
 hstore_delete(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	char	   *keyptr = VARDATA_ANY(key);
-	int			keylen = VARSIZE_ANY_EXHDR(key);
-	HStore	   *out = palloc(VARSIZE(hs));
-	char	   *bufs,
-			   *bufd,
-			   *ptrd;
-	HEntry	   *es,
-			   *ed;
-	int			i;
-	int			count = HS_COUNT(hs);
-	int			outcount = 0;
-
-	SET_VARSIZE(out, VARSIZE(hs));
-	HS_SETCOUNT(out, count);	/* temporary! */
+	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);
 
-	bufs = STRPTR(hs);
-	es = ARRPTR(hs);
-	bufd = ptrd = STRPTR(out);
-	ed = ARRPTR(out);
+	it = HStoreIteratorInit(VARDATA(in));
 
-	for (i = 0; i < count; ++i)
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		int			len = HS_KEYLEN(es, i);
-		char	   *ptrs = HS_KEY(es, bufs, i);
+		skipNested = true;
 
-		if (!(len == keylen && memcmp(ptrs, keyptr, keylen) == 0))
+		if ((r == WHS_ELEM || r == WHS_KEY) &&
+			(v.type == hsvString && keylen == v.string.len &&
+			 memcmp(keyptr, v.string.val, keylen) == 0))
 		{
-			int			vallen = HS_VALLEN(es, i);
+			if (r == WHS_KEY)
+				/* skip corresponding value */
+				HStoreIteratorGet(&it, &v, true);
 
-			HS_COPYITEM(ed, bufd, ptrd, ptrs, len, vallen, HS_VALISNULL(es, i));
-			++outcount;
+			continue;
 		}
+
+		res = pushHStoreValue(&toState, r, &v);
 	}
 
-	HS_FINALIZE(out, outcount, bufd, ptrd);
+	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);
+	}
 
 	PG_RETURN_POINTER(out);
 }
 
-
 PG_FUNCTION_INFO_V1(hstore_delete_array);
 Datum		hstore_delete_array(PG_FUNCTION_ARGS);
 Datum
 hstore_delete_array(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HStore	   *out = palloc(VARSIZE(hs));
-	int			hs_count = HS_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)
+	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)
 	{
-		/* return a copy of the input, unchanged */
-		memcpy(out, hs, VARSIZE(hs));
-		HS_FIXSIZE(out, hs_count);
-		HS_SETCOUNT(out, hs_count);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	/*
-	 * this is in effect a merge between hs and key_pairs, both of which are
-	 * already sorted by (keylen,key); we take keys from hs only
-	 */
+	it = HStoreIteratorInit(VARDATA(in));
 
-	for (i = j = 0; i < hs_count;)
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		int			difference;
 
-		if (j >= nkeys)
-			difference = -1;
-		else
+		if (skipNested == false)
 		{
-			int			skeylen = HS_KEYLEN(es, i);
-
-			if (skeylen == key_pairs[j].keylen)
-				difference = memcmp(HS_KEY(es, ps, i),
-									key_pairs[j].key,
-									key_pairs[j].keylen);
-			else
-				difference = (skeylen > key_pairs[j].keylen) ? 1 : -1;
+			Assert(v.type == hsvArray || v.type == hsvHash);
+			isHash = (v.type == hsvArray) ? false : true;
+			skipNested = true;
 		}
 
-		if (difference > 0)
-			++j;
-		else if (difference == 0)
-			++i, ++j;
-		else
+		if ((r == WHS_ELEM || r == WHS_KEY) && v.type == hsvString &&
+			i < a->array.nelems)
 		{
-			HS_COPYITEM(ed, bufd, pd,
-						HS_KEY(es, ps, i), HS_KEYLEN(es, i),
-						HS_VALLEN(es, i), HS_VALISNULL(es, i));
-			++outcount;
-			++i;
-		}
-	}
+			int diff;
 
-	HS_FINALIZE(out, outcount, bufd, pd);
+			if (isHash)
+			{
+				do {
+					diff = compareHStoreStringValue(&v, a->array.elems + i,
+													NULL);
 
-	PG_RETURN_POINTER(out);
-}
+					if (diff >= 0)
+						i++;
+				} while(diff > 0 && i < a->array.nelems);
+			}
+			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);
 
-PG_FUNCTION_INFO_V1(hstore_delete_hstore);
-Datum		hstore_delete_hstore(PG_FUNCTION_ARGS);
-Datum
-hstore_delete_hstore(PG_FUNCTION_ARGS)
+				continue;
+			}
+		}
+
+		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);
+	}
+
+	PG_RETURN_POINTER(out);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_delete_hstore);
+Datum		hstore_delete_hstore(PG_FUNCTION_ARGS);
+Datum
+hstore_delete_hstore(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HStore	   *hs2 = PG_GETARG_HS(1);
-	HStore	   *out = palloc(VARSIZE(hs));
-	int			hs_count = HS_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);
+	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));
 		PG_RETURN_POINTER(out);
 	}
 
-	/*
-	 * this is in effect a merge between hs and hs2, both of which are already
-	 * sorted by (keylen,key); we take keys from hs only; for equal keys, we
-	 * take the value from hs unless the values are equal
-	 */
+	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);
 
-	for (i = j = 0; i < hs_count;)
+	if (isHash1 == true && isHash2 == true)
 	{
-		int			difference;
+		bool			fin2 = false,
+						keyIsDef = false;
 
-		if (j >= hs2_count)
-			difference = -1;
-		else
+		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)
 		{
-			int			skeylen = HS_KEYLEN(es, i);
-			int			s2keylen = HS_KEYLEN(es2, j);
 
-			if (skeylen == s2keylen)
-				difference = memcmp(HS_KEY(es, ps, i),
-									HS_KEY(es2, ps2, j),
-									skeylen);
+			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);
+
+	if (r == WHS_BEGIN_ARRAY)
+	{
+		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
-				difference = (skeylen > s2keylen) ? 1 : -1;
+				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;
 		}
 
-		if (difference > 0)
-			++j;
-		else if (difference == 0)
+		if (level == path_len - 1)
 		{
-			int			svallen = HS_VALLEN(es, i);
-			int			snullval = HS_VALISNULL(es, i);
+			/* last level in path, skip all elem */
+			r = HStoreIteratorGet(it, &v, true);
+			Assert(r == WHS_ELEM);
+		}
+		else
+		{
+			res = deletePathDo(it, path_elems, path_nulls, path_len, st,
+							   level + 1);
+		}
 
-			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)))
+		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)
 			{
-				HS_COPYITEM(ed, bufd, pd,
-							HS_KEY(es, ps, i), HS_KEYLEN(es, i),
-							svallen, snullval);
-				++outcount;
+				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);
+		}
+
+		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);
+
+			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)
+			{
+				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;
 			}
-			++i, ++j;
+
+			pushHStoreValue(st, r, &k);
+			r = HStoreIteratorGet(it, &v, true);
+			Assert(r == WHS_VALUE);
+			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) /* just a string or null */
+	{
+		pushHStoreValue(st, r, &v);
+		res = (void*)0x01; /* dummy value */
+	}
+	else
+	{
+		elog(PANIC, "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)
 		{
-			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 (i++ == idx)
+			{
+				if (r == WHS_KEY)
+					HStoreIteratorGet(&it, &v, true); /* skip value */
+				continue;
+			}
 		}
+
+		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(PANIC,"Could not convert to string");
+	}
+}
+
+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
+	{
+		int 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(PANIC, "impossible state");
+	}
 
-	PG_RETURN_POINTER(out);
+	return res;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_concat);
-Datum		hstore_concat(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_replace);
+Datum		hstore_replace(PG_FUNCTION_ARGS);
 Datum
-hstore_concat(PG_FUNCTION_ARGS)
+hstore_replace(PG_FUNCTION_ARGS)
 {
-	HStore	   *s1 = PG_GETARG_HS(0);
-	HStore	   *s2 = PG_GETARG_HS(1);
-	HStore	   *out = palloc(VARSIZE(s1) + VARSIZE(s2));
-	char	   *ps1,
-			   *ps2,
-			   *bufd,
-			   *pd;
-	HEntry	   *es1,
-			   *es2,
-			   *ed;
-	int			s1idx;
-	int			s2idx;
-	int			s1count = 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);
+	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));
 		PG_RETURN_POINTER(out);
 	}
 
-	if (s2count == 0)
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &path_elems, &path_nulls, &path_len);
+
+	if (path_len == 0)
 	{
-		/* return a copy of the input, unchanged */
-		memcpy(out, s1, VARSIZE(s1));
-		HS_FIXSIZE(out, s1count);
-		HS_SETCOUNT(out, s1count);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	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)
+	if (HS_ROOT_COUNT(newval) == 0)
+	{
+		value.type = hsvNull;
+		value.size = sizeof(HEntry);
+	}
+	else
 	{
-		int			difference;
+		value.type = hsvBinary;
+		value.binary.data = VARDATA(newval);
+		value.binary.len = VARSIZE_ANY_EXHDR(newval);
+		value.size = value.binary.len + sizeof(HEntry);
+	}
 
-		if (s1idx >= s1count)
-			difference = 1;
-		else if (s2idx >= s2count)
-			difference = -1;
-		else
-		{
-			int			s1keylen = HS_KEYLEN(es1, s1idx);
-			int			s2keylen = HS_KEYLEN(es2, s2idx);
+	it = HStoreIteratorInit(VARDATA(in));
 
-			if (s1keylen == s2keylen)
-				difference = memcmp(HS_KEY(es1, ps1, s1idx),
-									HS_KEY(es2, ps2, s2idx),
-									s1keylen);
-			else
-				difference = (s1keylen > s2keylen) ? 1 : -1;
-		}
+	res = replacePathDo(&it, path_elems, path_nulls, path_len, &st, 0, &value);
 
-		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;
-		}
+	if (res == NULL)
+	{
+		SET_VARSIZE(out, VARHDRSZ);
 	}
+	else
+	{
+		int				sz;
 
-	HS_FINALIZE(out, outcount, bufd, pd);
+		sz = compressHStore(res, VARDATA(out));
+		SET_VARSIZE(out, sz + 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)
+static HStoreValue*
+concatPathDo(HStoreIterator **it, Datum *path_elems,
+			 bool *path_nulls, int path_len,
+			 ToHStoreState  **st, int level, HStoreIterator	*toConcat)
 {
-	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;
+	HStoreValue v, *res = NULL;
+	int			r;
 
-	deconstruct_array(key_array,
-					  TEXTOID, -1, false, 'i',
-					  &key_datums, &key_nulls, &key_count);
+	r = HStoreIteratorGet(it, &v, false);
 
-	if (key_count == 0)
+	if (r == WHS_BEGIN_ARRAY)
 	{
-		aout = construct_empty_array(TEXTOID);
-		PG_RETURN_POINTER(aout);
-	}
+		int		idx, i;
+		uint32	n = v.array.nelems;
 
-	out_datums = palloc(sizeof(Datum) * key_count);
-	out_nulls = palloc(sizeof(bool) * key_count);
+		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;
+		}
 
-	for (i = 0; i < key_count; ++i)
-	{
-		text	   *key = (text *) DatumGetPointer(key_datums[i]);
-		int			idx;
+		if (idx > n)
+			idx = n;
 
-		if (key_nulls[i])
-			idx = -1;
-		else
-			idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ);
+		pushHStoreValue(st, r, &v);
 
-		if (idx < 0 || HS_VALISNULL(entries, idx))
+		for(i=0; i<n; i++)
 		{
-			out_nulls[i] = true;
-			out_datums[i] = (Datum) 0;
+			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);
+			}
 		}
-		else
+
+		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++)
 		{
-			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, &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);
+			}
 		}
-	}
 
-	aout = construct_md_array(out_datums, out_nulls,
-							  ARR_NDIM(key_array),
-							  ARR_DIMS(key_array),
-							  ARR_LBOUND(key_array),
-							  TEXTOID, -1, false, 'i');
+		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(PANIC, "impossible state");
+	}
 
-	PG_RETURN_POINTER(aout);
+	return res;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_slice_to_hstore);
-Datum		hstore_slice_to_hstore(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_deep_concat);
+Datum		hstore_deep_concat(PG_FUNCTION_ARGS);
 Datum
-hstore_slice_to_hstore(PG_FUNCTION_ARGS)
+hstore_deep_concat(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *ptr = STRPTR(hs);
-	ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(1);
-	HStore	   *out;
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(key_array, &nkeys);
-	Pairs	   *out_pairs;
-	int			bufsiz;
-	int			lastidx = 0;
-	int			i;
-	int			out_count = 0;
-
-	if (nkeys == 0)
+	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")));
+
+	if (HS_ROOT_COUNT(in) == 0 || HS_ROOT_COUNT(newval) == 0)
 	{
-		out = hstorePairs(NULL, 0, 0);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	out_pairs = palloc(sizeof(Pairs) * nkeys);
-	bufsiz = 0;
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &path_elems, &path_nulls, &path_len);
 
-	/*
-	 * 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.
-	 */
+	it1 = HStoreIteratorInit(VARDATA(in));
+	it2 = HStoreIteratorInit(VARDATA(newval));
 
-	for (i = 0; i < nkeys; ++i)
-	{
-		int			idx = hstoreFindKey(hs, &lastidx,
-									  key_pairs[i].key, key_pairs[i].keylen);
+	if (path_len == 0)
+		res = IteratorConcat(&it1, &it2, &st);
+	else
+		res = concatPathDo(&it1, path_elems, path_nulls, path_len, &st, 0, it2);
 
-		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;
-		}
+	if (res == NULL)
+	{
+		SET_VARSIZE(out, VARHDRSZ);
 	}
+	else
+	{
+		int				sz;
 
-	/*
-	 * we don't use uniquePairs here because we know that the pairs list is
-	 * already sorted and uniq'ed.
-	 */
-
-	out = hstorePairs(out_pairs, out_count, bufsiz);
+		sz = compressHStore(res, VARDATA(out));
+		SET_VARSIZE(out, sz + VARHDRSZ);
+	}
 
 	PG_RETURN_POINTER(out);
 }
 
-
 PG_FUNCTION_INFO_V1(hstore_akeys);
 Datum		hstore_akeys(PG_FUNCTION_ARGS);
 Datum
 hstore_akeys(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	Datum	   *d;
-	ArrayType  *a;
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *base = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			i;
-
-	if (count == 0)
+	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))
 	{
 		a = construct_empty_array(TEXTOID);
 		PG_RETURN_POINTER(a);
 	}
 
-	d = (Datum *) palloc(sizeof(Datum) * count);
+	d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
 
-	for (i = 0; i < count; ++i)
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		text	   *item = cstring_to_text_with_len(HS_KEY(entries, base, i),
-													HS_KEYLEN(entries, i));
+		skipNested = true;
 
-		d[i] = PointerGetDatum(item);
+		if ((r == WHS_ELEM && v.type != hsvNull) || r == WHS_KEY)
+			d[i++] = PointerGetDatum(HStoreValueToText(&v));
 	}
 
-	a = construct_array(d, count,
+	a = construct_array(d, i,
 						TEXTOID, -1, false, 'i');
 
 	PG_RETURN_POINTER(a);
@@ -727,43 +2037,40 @@ Datum		hstore_avals(PG_FUNCTION_ARGS);
 Datum
 hstore_avals(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	Datum	   *d;
-	bool	   *nulls;
-	ArrayType  *a;
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *base = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			lb = 1;
-	int			i;
-
-	if (count == 0)
+	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))
 	{
 		a = construct_empty_array(TEXTOID);
 		PG_RETURN_POINTER(a);
 	}
 
-	d = (Datum *) palloc(sizeof(Datum) * count);
-	nulls = (bool *) palloc(sizeof(bool) * count);
+	d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
+	nulls = (bool *) palloc(sizeof(bool) * HS_ROOT_COUNT(hs));
 
-	for (i = 0; i < count; ++i)
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		if (HS_VALISNULL(entries, i))
-		{
-			d[i] = (Datum) 0;
-			nulls[i] = true;
-		}
-		else
-		{
-			text	   *item = cstring_to_text_with_len(HS_VAL(entries, base, i),
-													  HS_VALLEN(entries, i));
+		skipNested = true;
 
-			d[i] = PointerGetDatum(item);
-			nulls[i] = false;
+		if (r == WHS_ELEM || r == WHS_VALUE)
+		{
+			d[i] = PointerGetDatum(HStoreValueToText(&v));
+			nulls[i] = (DatumGetPointer(d[i]) == NULL) ? true : false;
+			i++;
 		}
 	}
 
-	a = construct_md_array(d, nulls, 1, &count, &lb,
+	a = construct_md_array(d, nulls, 1, &i, &lb,
 						   TEXTOID, -1, false, 'i');
 
 	PG_RETURN_POINTER(a);
@@ -773,44 +2080,53 @@ hstore_avals(PG_FUNCTION_ARGS)
 static ArrayType *
 hstore_to_array_internal(HStore *hs, int ndims)
 {
-	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;
+	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;
 
 	Assert(ndims < 3);
 
 	if (count == 0 || ndims == 0)
 		return construct_empty_array(TEXTOID);
 
-	out_size[0] = count * 2 / ndims;
+	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_datums = palloc(sizeof(Datum) * count * 2);
 	out_nulls = palloc(sizeof(bool) * count * 2);
 
-	for (i = 0; i < count; ++i)
-	{
-		text	   *key = cstring_to_text_with_len(HS_KEY(entries, base, i),
-												   HS_KEYLEN(entries, i));
+	it = HStoreIteratorInit(VARDATA(hs));
 
-		out_datums[i * 2] = PointerGetDatum(key);
-		out_nulls[i * 2] = false;
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
 
-		if (HS_VALISNULL(entries, i))
-		{
-			out_datums[i * 2 + 1] = (Datum) 0;
-			out_nulls[i * 2 + 1] = true;
-		}
-		else
+		switch(r)
 		{
-			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;
+			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;
 		}
 	}
 
@@ -850,222 +2166,526 @@ hstore_to_matrix(PG_FUNCTION_ARGS)
  * there was no explanatory comment in the original code. --AG)
  */
 
-static void
-setup_firstcall(FuncCallContext *funcctx, HStore *hs,
-				FunctionCallInfoData *fcinfo)
-{
-	MemoryContext oldcontext;
-	HStore	   *st;
+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;
+	}
+
+	while(st->level >= 0)
+	{
+		uint32	header;
 
-	oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+		v = NULL;
+		if (st->path[st->level].v.type != hsvBinary)
+		{
+			st->level--;
+			continue;
+		}
 
-	st = (HStore *) palloc(VARSIZE(hs));
-	memcpy(st, hs, VARSIZE(hs));
+		header = *(uint32*)st->path[st->level].v.binary.data;
 
-	funcctx->user_fctx = (void *) st;
+		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;
 
-	if (fcinfo)
-	{
-		TupleDesc	tupdesc;
+				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;
+					}
+				}
 
-		/* 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");
+				v = getHStoreValue(st->path[st->level].v.binary.data,
+								   HS_FLAG_ARRAY, ith);
+			}
+			else
+			{
+				st->level--;
+				continue;
+			}
+		}
+		else
+		{
+			elog(PANIC, "impossible state");
+		}
 
-		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+		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;
+		}
 	}
 
-	MemoryContextSwitchTo(oldcontext);
+	return v;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_skeys);
-Datum		hstore_skeys(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_svals_path);
+Datum		hstore_svals_path(PG_FUNCTION_ARGS);
 Datum
-hstore_skeys(PG_FUNCTION_ARGS)
+hstore_svals_path(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	HStoreValue			*v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, NULL);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	if ((v = getNextValsPath(st)) != NULL)
 	{
-		HEntry	   *entries = ARRPTR(hs);
-		text	   *item;
+		text	*item = HStoreValueToText(v);
 
-		item = cstring_to_text_with_len(HS_KEY(entries, STRPTR(hs), i),
-										HS_KEYLEN(entries, i));
-
-		SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
+		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);
+PG_FUNCTION_INFO_V1(hstore_hvals_path);
+Datum		hstore_hvals_path(PG_FUNCTION_ARGS);
 Datum
-hstore_svals(PG_FUNCTION_ARGS)
+hstore_hvals_path(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	HStoreValue 		*v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, NULL);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0),
+							 PG_GETARG_ARRAYTYPE_P(1), NULL);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	if ((v = getNextValsPath(st)) != NULL)
 	{
-		HEntry	   *entries = ARRPTR(hs);
+		HStore	   *item = HStoreValueToHStore(v);
 
-		if (HS_VALISNULL(entries, i))
-		{
-			ReturnSetInfo *rsi;
-
-			/* ugly ugly ugly. why no macro for this? */
-			(funcctx)->call_cntr++;
-			rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-			rsi->isDone = ExprMultipleResult;
-			PG_RETURN_NULL();
-		}
+		if (item == NULL)
+			SRF_RETURN_NEXT_NULL(funcctx);
 		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_contains);
-Datum		hstore_contains(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_each);
+Datum		hstore_each(PG_FUNCTION_ARGS);
 Datum
-hstore_contains(PG_FUNCTION_ARGS)
+hstore_each(PG_FUNCTION_ARGS)
 {
-	HStore	   *val = PG_GETARG_HS(0);
-	HStore	   *tmpl = PG_GETARG_HS(1);
-	bool		res = true;
-	HEntry	   *te = ARRPTR(tmpl);
-	char	   *tstr = STRPTR(tmpl);
-	HEntry	   *ve = ARRPTR(val);
-	char	   *vstr = STRPTR(val);
-	int			tcount = HS_COUNT(tmpl);
-	int			lastidx = 0;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	int					r;
+	HStoreValue			v;
 
-	/*
-	 * 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
-	 */
+	if (SRF_IS_FIRSTCALL())
+	{
+		funcctx = SRF_FIRSTCALL_INIT();
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	for (i = 0; res && i < tcount; ++i)
+	while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
 	{
-		int			idx = hstoreFindKey(val, &lastidx,
-									  HS_KEY(te, tstr, i), HS_KEYLEN(te, i));
+		Datum		res,
+					dvalues[2] = {0, 0};
+		bool		nulls[2] = {false, false};
+		text	   *item;
+		HeapTuple	tuple;
 
-		if (idx >= 0)
+		if (r == WHS_ELEM)
 		{
-			bool		nullval = HS_VALISNULL(te, i);
-			int			vallen = HS_VALLEN(te, i);
+			nulls[0] = true;
 
-			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;
+			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);
 		}
 		else
-			res = false;
-	}
+		{
+			continue;
+		}
 
-	PG_RETURN_BOOL(res);
-}
+		tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
+		res = HeapTupleGetDatum(tuple);
 
+		SRF_RETURN_NEXT(funcctx, PointerGetDatum(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)
-										));
+	SRF_RETURN_DONE(funcctx);
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_each);
-Datum		hstore_each(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_each_hstore);
+Datum		hstore_each_hstore(PG_FUNCTION_ARGS);
 Datum
-hstore_each(PG_FUNCTION_ARGS)
+hstore_each_hstore(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	int					r;
+	HStoreValue			v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, fcinfo);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
 	{
-		HEntry	   *entries = ARRPTR(hs);
-		char	   *ptr = STRPTR(hs);
 		Datum		res,
-					dvalues[2];
+					dvalues[2] = {0, 0};
 		bool		nulls[2] = {false, false};
 		text	   *item;
+		HStore		*hitem;
 		HeapTuple	tuple;
 
-		item = cstring_to_text_with_len(HS_KEY(entries, ptr, i),
-										HS_KEYLEN(entries, i));
-		dvalues[0] = PointerGetDatum(item);
+		if (r == WHS_ELEM)
+		{
+			nulls[0] = true;
 
-		if (HS_VALISNULL(entries, i))
+			hitem = HStoreValueToHStore(&v);
+			if (hitem == NULL)
+				nulls[1] = true;
+			else
+				dvalues[1] = PointerGetDatum(hitem);
+		}
+		else if (r == WHS_KEY)
 		{
-			dvalues[1] = (Datum) 0;
-			nulls[1] = true;
+			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);
 		}
 		else
 		{
-			item = cstring_to_text_with_len(HS_VAL(entries, ptr, i),
-											HS_VALLEN(entries, i));
-			dvalues[1] = PointerGetDatum(item);
+			continue;
 		}
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
@@ -1077,6 +2697,183 @@ hstore_each(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(PANIC, "impossible state");
+	}
+
+	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
@@ -1089,72 +2886,28 @@ Datum		hstore_cmp(PG_FUNCTION_ARGS);
 Datum
 hstore_cmp(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs1 = PG_GETARG_HS(0);
-	HStore	   *hs2 = PG_GETARG_HS(1);
-	int			hcount1 = HS_COUNT(hs1);
-	int			hcount2 = HS_COUNT(hs2);
-	int			res = 0;
+	HStore	   		*hs1 = PG_GETARG_HS(0);
+	HStore	   		*hs2 = PG_GETARG_HS(1);
+	int				res;
 
-	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) || HS_ISEMPTY(hs2))
 	{
-		/* 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(hs1))
 		{
-			if (len1 > len2)
-				res = 1;
-			else if (len1 < len2)
-				res = -1;
-			else if (hcount1 > hcount2)
-				res = 1;
-			else if (hcount2 > hcount1)
-				res = -1;
+			if (HS_ISEMPTY(hs2))
+				res = 0;
 			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;
-				}
-			}
+				res = -1;
 		}
 		else
 		{
-			res = (res > 0) ? 1 : -1;
+			res = 1;
 		}
 	}
+	else
+	{
+		res = compareHStoreBinaryValue(VARDATA(hs1), VARDATA(hs2));
+	}
 
 	/*
 	 * this is a btree support function; this is one of the few places where
@@ -1248,17 +3001,61 @@ 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
new file mode 100644
index 0000000..3a357e1
--- /dev/null
+++ b/contrib/hstore/hstore_scan.l
@@ -0,0 +1,311 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 (JsonbStringIsNumber(scanstring.val, scanstring.len))
+	{
+		/* 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;
+}
+
diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql
index 9518f56..10afc45 100644
--- a/contrib/hstore/sql/hstore.sql
+++ b/contrib/hstore/sql/hstore.sql
@@ -39,6 +39,9 @@ select 'aa=>"bb" ,cc=>dd'::hstore;
 select 'aa=>null'::hstore;
 select 'aa=>NuLl'::hstore;
 select 'aa=>"NuLl"'::hstore;
+select 'aa=>nul'::hstore;
+select 'aa=>NuL'::hstore;
+select 'aa=>"NuL"'::hstore;
 
 select e'\\=a=>q=w'::hstore;
 select e'"=a"=>q\\=w'::hstore;
@@ -213,7 +216,7 @@ select hstore(v) from testhstore1 v;
 select hstore(null::testhstore0);
 select hstore(null::testhstore1);
 select pg_column_size(hstore(v))
-         = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore)
+         = pg_column_size('a=>"1", b=>"foo", c=>1.2, d=>"3", e=>"0"'::hstore)
   from testhstore1 v;
 select populate_record(v, hstore('c', '3.45')) from testhstore1 v;
 select populate_record(v, hstore('d', '3.45')) from testhstore1 v;
@@ -299,6 +302,8 @@ select count(*) from testhstore where h ? 'public';
 select count(*) from testhstore where h ?| ARRAY['public','disabled'];
 select count(*) from testhstore where h ?& ARRAY['public','disabled'];
 
+RESET enable_seqscan;
+
 drop index hidx;
 create index hidx on testhstore using gin (h);
 set enable_seqscan=off;
@@ -310,6 +315,8 @@ select count(*) from testhstore where h ? 'public';
 select count(*) from testhstore where h ?| ARRAY['public','disabled'];
 select count(*) from testhstore where h ?& ARRAY['public','disabled'];
 
+RESET enable_seqscan;
+
 select count(*) from (select (each(h)).key from testhstore) as wow ;
 select key, count(*) from (select (each(h)).key from testhstore) as wow group by key order by count desc, key;
 
@@ -323,6 +330,9 @@ select count(*) from (select h from (select * from testhstore union all select *
 select distinct * from (values (hstore '' || ''),('')) v(h);
 set enable_sort = true;
 
+RESET enable_hashagg;
+RESET enable_sort;
+
 -- btree
 drop index hidx;
 create index hidx on testhstore using btree (h);
@@ -331,6 +341,18 @@ set enable_seqscan=off;
 select count(*) from testhstore where h #># 'p=>1';
 select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
 
+--gin hash
+drop index hidx;
+create index hidx on testhstore using gin (h gin_hstore_hash_ops);
+set enable_seqscan=off;
+
+select count(*) from testhstore where h @> 'wait=>NULL';
+select count(*) from testhstore where h @> 'wait=>CC';
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+
+RESET enable_seqscan;
+drop index hidx;
+
 -- json
 select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
diff --git a/contrib/hstore/sql/nested.sql b/contrib/hstore/sql/nested.sql
new file mode 100644
index 0000000..71a692e
--- /dev/null
+++ b/contrib/hstore/sql/nested.sql
@@ -0,0 +1,478 @@
+
+SELECT 'ff => {a=>12, b=>16}'::hstore;
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123'::hstore;
+
+SELECT 'aa => {a,aaa}, qq=>{ a=>12, b=>16 , c=> { c1, c2}, d=>{d1=>d1, d2=>d2, d1=>d3} }'::hstore;
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+
+SELECT 'ff => {a,aaa}'::hstore;
+
+
+select 'null'::hstore;
+select '{null}'::hstore;
+select ''::hstore;
+select '{}'::hstore;
+
+--test optional outer braces
+SELECT	'a=>1'::hstore;
+SELECT	'{a=>1}'::hstore;
+SELECT	'{a,b}'::hstore;
+SELECT	'{a,{b}}'::hstore;
+SELECT	'{{a},b}'::hstore;
+SELECT	'{a,{b},{c}}'::hstore;
+SELECT	'{{a},{b},c}'::hstore;
+SELECT	'{{a},b,{c}}'::hstore;
+SELECT	'{a,{b=>1}}'::hstore;
+SELECT	'{{a},{b=>1}}'::hstore;
+SELECT	'a'::hstore;
+SELECT	'{a}'::hstore;
+SELECT	''::hstore;
+SELECT	'{}'::hstore;
+
+--nested json
+
+SELECT	hstore_to_json('a=>1');
+SELECT	hstore_to_json('{a=>1}');
+SELECT	hstore_to_json('{a,b}');
+SELECT	hstore_to_json('{a,{b}}');
+SELECT	hstore_to_json('{{a},b}');
+SELECT	hstore_to_json('{a,{b},{c}}');
+SELECT	hstore_to_json('{{a},{b},c}');
+SELECT	hstore_to_json('{{a},b,{c}}');
+SELECT	hstore_to_json('{a,{b=>1}}');
+SELECT	hstore_to_json('{{a},{b=>1}}');
+SELECT	hstore_to_json('{{a},{b=>1},{c}}');
+SELECT	hstore_to_json('a');
+SELECT	hstore_to_json('{a}');
+SELECT	hstore_to_json('');
+SELECT	hstore_to_json('{}');
+
+SELECT hstore_to_json('"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore);
+
+--
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'ff', 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'qq', 
+	   ('ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'Y') IS NULL AS t, 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'x'; 
+
+SELECT '[ a, b, c, d]'::hstore -> 'a';
+--
+
+CREATE TABLE testtype (i int, h hstore, a int[], j json);
+INSERT INTO testtype VALUES (1, 'a=>1', '{1,2,3}', '{"x": 2}');
+
+SELECT populate_record(v, 'i=>2'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, a=>{7,8,9}'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}'::hstore) FROM testtype v;
+
+--complex delete
+
+SELECT 'b=>{a,c}'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>1'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+SELECT '[2,3,a]'::hstore - 'a'::text;
+SELECT '[a,2,3,a]'::hstore - 'a'::text;
+SELECT '[a,a]'::hstore - 'a'::text;
+SELECT '[a]'::hstore - 'a'::text;
+SELECT 'a=>1'::hstore - 'a'::text;
+SELECT ''::hstore - 'a'::text;
+
+SELECT '{a, 1 , b,2, c,3}'::hstore - ARRAY['d','b'];
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>23'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>{1,2}'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'a=>{1,2}'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v=>23'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,23]'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,{1,2}]'::hstore;
+
+--joining
+
+SELECT 'aa=>1 , b=>2, cq=>3'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+SELECT '{aa,1 , b,2, cq,3}'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+
+--slice
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['b','c']);
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+SELECT slice_array(hstore 'aa=>1, b=>{2=>1}, c=>{1,2}', ARRAY['b','c']);
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['g','h','i']);
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['b','c']);
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+SELECT slice(hstore '{aa=>1, b=>{2=>1}, c=>{1,2}}', ARRAY['b','c']);
+
+--to array
+SELECT %% 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL';
+SELECT %% '{aa,1, cq,l, b,g, fg,NULL}';
+SELECT hstore_to_matrix( 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL');
+SELECT hstore_to_matrix( '{aa,1, cq,l, b,g, fg,NULL}');
+
+
+--contains
+SELECT 'a=>b'::hstore @> 'a=>b, c=>b';
+SELECT 'a=>b, c=>b'::hstore @> 'a=>b';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{2,1}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1=>2}';
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1=>2}';
+SELECT '{a,b}'::hstore @> '{a,b, c,b}';
+SELECT '{a,b, c,b}'::hstore @> '{a,b}';
+SELECT '{a,b, c,{1,2}}'::hstore @> '{a,{1,2}}';
+SELECT '{a,b, c,{1,2}}'::hstore @> '{b,{1,2}}';
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1}';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{2}';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{3}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{c=>3}}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4}}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},3}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},1}';
+
+-- %>
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'n';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'a';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'b';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'c';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd' %> '1';
+
+SELECT '[1,2,3,{a,b}]'::hstore %> '1';
+SELECT '["1",2,3,{a,b}]'::hstore %> '1';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 0;
+
+-- ->
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 0;
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -6;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -1;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -6;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -1;
+
+-- #>
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{a}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -4}';
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{0}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{3}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4,5}';
+
+-- #%>
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{a}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -4}';
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{0}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{3}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4,5}';
+
+-- ?
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 0;
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -6;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -1;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -6;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -1;
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{0}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{a}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{b}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 0}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 1}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 2}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 3}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -1}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -2}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -3}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -4}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -5}'::text[];
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{0}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{3}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4,5}'::text[];
+
+--deep delete
+
+SELECT 'a=>1'::hstore #- '{x}';
+SELECT 'a=>1'::hstore #- '{a}';
+SELECT 'a=>1'::hstore #- '{NULL}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c}';
+SELECT 'a=>1'::hstore #- '{x,1}';
+SELECT 'a=>1'::hstore #- '{a,1}';
+SELECT 'a=>1'::hstore #- '{NULL,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c,1}';
+
+SELECT '[a]'::hstore #- '{2}';
+SELECT '[a]'::hstore #- '{1}';
+SELECT '[a]'::hstore #- '{0}';
+SELECT '[a]'::hstore #- '{-1}';
+SELECT '[a]'::hstore #- '{-2}';
+
+SELECT '[a,b,c]'::hstore #- '{3}';
+SELECT '[a,b,c]'::hstore #- '{2}';
+SELECT '[a,b,c]'::hstore #- '{1}';
+SELECT '[a,b,c]'::hstore #- '{0}';
+SELECT '[a,b,c]'::hstore #- '{-1}';
+SELECT '[a,b,c]'::hstore #- '{-2}';
+SELECT '[a,b,c]'::hstore #- '{-3}';
+SELECT '[a,b,c]'::hstore #- '{-4}';
+
+SELECT '[a,b,c]'::hstore #- '{0,0}';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{x}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{a}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d}';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}' #- '{b, -1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 2}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, -2}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}' #- '{d, 1, 0}';
+
+-- delete(int)
+
+SELECT '[a,b,c]'::hstore - 3;
+SELECT '[a,b,c]'::hstore - 2;
+SELECT '[a,b,c]'::hstore - 1;
+SELECT '[a,b,c]'::hstore - 0;
+SELECT '[a,b,c]'::hstore - -1;
+SELECT '[a,b,c]'::hstore - -2;
+SELECT '[a,b,c]'::hstore - -3;
+SELECT '[a,b,c]'::hstore - -4;
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 3;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 2;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 1;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 0;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -1;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -2;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -3;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -4;
+
+--replace
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b,-1}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1,0}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,NULL,0}', '{1,2,3}');
+
+--deep concat
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'n=>not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'n=>not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{not_null}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'b=>{3,4}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b}', '{3,4}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '{4,5}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '4=>5');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d}', '2=>{4,5}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{NULL,1}', '4=>5');
+
+--cast 
+
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::text)::hstore AS err;
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json)::hstore AS ok;
+
+--hvals
+
+SELECT q->'tags' FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore) AS q;
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+
+--svals path
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+
+--each
+
+SELECT * FROM each('a=>b, c=>cc'::hstore) AS q;
+SELECT * FROM each('[a, b, c, cc]'::hstore) AS q;
+SELECT * FROM each('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+
+SELECT * FROM each_hstore('a=>b, c=>cc'::hstore) AS q;
+SELECT * FROM each_hstore('[a, b, c, cc]'::hstore) AS q;
+SELECT * FROM each_hstore('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+
+--decoration
+
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]]'::hstore AS h, '[a, {b=>c}, [c, d, e]]'::hstore AS a;
+
+SET hstore.pretty_print = true;
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]], e=>[1,2,3,4], f=>g, g=>j'::hstore AS h, 
+	   '[a, {b=>c, c=>d}, [c, d, e, [1,2], h, {f=>g, g=>f}]]'::hstore AS a;
+RESET hstore.pretty_print;
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore);
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, array_curly_braces := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, loose := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, array_curly_braces := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, array_curly_braces := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true, loose := true);
diff --git a/contrib/hstore/sql/types.sql b/contrib/hstore/sql/types.sql
new file mode 100644
index 0000000..ac0b206
--- /dev/null
+++ b/contrib/hstore/sql/types.sql
@@ -0,0 +1,147 @@
+SELECT '"foo"=>true'::hstore;
+SELECT 'foo=>true'::hstore;
+SELECT '"true"=>true'::hstore;
+SELECT 'true=>true'::hstore;
+SELECT '"t"=>true'::hstore;
+SELECT 't=>true'::hstore;
+SELECT '"false"=>true'::hstore;
+SELECT 'false=>true'::hstore;
+SELECT '"f"=>true'::hstore;
+SELECT 'f=>true'::hstore;
+
+SELECT '"foo"=>false'::hstore;
+SELECT 'foo=>false'::hstore;
+SELECT '"false"=>false'::hstore;
+SELECT 'false=>false'::hstore;
+SELECT '"t"=>false'::hstore;
+SELECT 't=>false'::hstore;
+SELECT '"false"=>false'::hstore;
+SELECT 'false=>false'::hstore;
+SELECT '"f"=>false'::hstore;
+SELECT 'f=>false'::hstore;
+
+SELECT '"1"=>x'::hstore;
+SELECT '1=>x'::hstore;
+SELECT 'foo=>1'::hstore;
+SELECT 'foo=>1.'::hstore;
+SELECT 'foo=>1.0'::hstore;
+SELECT 'foo=>1.01'::hstore;
+SELECT 'foo=>1.01e'::hstore;
+SELECT 'foo=>1.01e1'::hstore;
+SELECT 'foo=>1.01e+1'::hstore;
+SELECT 'foo=>1.01e-1'::hstore;
+SELECT 'foo=>.1'::hstore;
+SELECT 'foo=>.1e'::hstore;
+SELECT 'foo=>.1e1'::hstore;
+SELECT 'foo=>.1e+1'::hstore;
+SELECT 'foo=>.1e-1'::hstore;
+
+SELECT 'foo=>+1'::hstore;
+SELECT 'foo=>+1.'::hstore;
+SELECT 'foo=>+1.0'::hstore;
+SELECT 'foo=>+1.01'::hstore;
+SELECT 'foo=>+1.01e'::hstore;
+SELECT 'foo=>+1.01e1'::hstore;
+SELECT 'foo=>+1.01e+1'::hstore;
+SELECT 'foo=>+1.01e-1'::hstore;
+SELECT 'foo=>+.1'::hstore;
+SELECT 'foo=>+.1e'::hstore;
+SELECT 'foo=>+.1e1'::hstore;
+SELECT 'foo=>+.1e+1'::hstore;
+SELECT 'foo=>+.1e-1'::hstore;
+
+SELECT 'foo=>-1'::hstore;
+SELECT 'foo=>-1.'::hstore;
+SELECT 'foo=>-1.0'::hstore;
+SELECT 'foo=>-1.01'::hstore;
+SELECT 'foo=>-1.01e'::hstore;
+SELECT 'foo=>-1.01e1'::hstore;
+SELECT 'foo=>-1.01e+1'::hstore;
+SELECT 'foo=>-1.01e-1'::hstore;
+SELECT 'foo=>-.1'::hstore;
+SELECT 'foo=>-.1e'::hstore;
+SELECT 'foo=>-.1e1'::hstore;
+SELECT 'foo=>-.1e+1'::hstore;
+SELECT 'foo=>-.1e-1'::hstore;
+
+SELECT 'foo=>1e2000'::hstore;
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'foo';
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'bar';
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 0;
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 1;
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'foo';
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'bar';
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 0;
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 1;
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 0}';
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 1}';
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'foo';
+SELECT 'foo=>t, bar=>x'::hstore ?> 'bar';
+SELECT 'foo=>t, bar=>x'::hstore ?> 0;
+SELECT 'foo=>t, bar=>x'::hstore ?> 1;
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'foo';
+SELECT '[foo, t, bar, x]'::hstore ?> 'bar';
+SELECT '[foo, t, bar, x]'::hstore ?> 0;
+SELECT '[foo, t, bar, x]'::hstore ?> 1;
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 0}';
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 1}';
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'foo';
+SELECT 'foo=>f, bar=>x'::hstore ?> 'bar';
+SELECT 'foo=>f, bar=>x'::hstore ?> 0;
+SELECT 'foo=>f, bar=>x'::hstore ?> 1;
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'foo';
+SELECT '[foo, f, bar, x]'::hstore ?> 'bar';
+SELECT '[foo, f, bar, x]'::hstore ?> 0;
+SELECT '[foo, f, bar, x]'::hstore ?> 1;
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 0}';
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 1}';
+
+
+SELECT hstore_typeof('a=>b') AS hash;
+SELECT hstore_typeof('{a=>b}') AS hash;
+SELECT hstore_typeof('{a, b}') AS array;
+SELECT hstore_typeof('{{a=>b}}') AS array;
+SELECT hstore_typeof('[a, b]') AS array;
+SELECT hstore_typeof('') AS "NULL";
+SELECT hstore_typeof('NULL') AS "null";
+SELECT hstore_typeof('1.0') AS numeric;
+SELECT hstore_typeof('t') AS bool;
+SELECT hstore_typeof('f') AS bool;
+
+SELECT hstore('xxx', 't'::bool);
+SELECT hstore('xxx', 'f'::bool);
+
+SELECT hstore('xxx', 3.14);
+SELECT hstore('xxx', 3.14::numeric);
+SELECT hstore('xxx', '3.14'::numeric);
+
+SELECT hstore(NULL);
+SELECT hstore('NULL');
+
+SELECT hstore('t'::bool) AS "true", hstore('f'::bool) AS "false";
+
+SELECT hstore(3.14), hstore(3.14::numeric), hstore('3.14'::numeric);
+
+SELECT hstore('xxx', 'foo=>t, bar=>3.14, zzz=>xxx'::hstore);
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int2[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int4[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int8[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float4[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float8[]);
+SELECT array_to_hstore('{{1,1,f},{f,t,NULL}}'::bool[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::text[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::varchar[]);
+
+SELECT array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]);
+SELECT hstore('array', array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]));
+
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index e5b9e66..4b854f9 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -12,7 +12,7 @@
    <productname>PostgreSQL</> also provides event triggers.  Unlike regular
    triggers, which are attached to a single table and capture only DML events,
    event triggers are global to a particular database and are capable of
-   capturing DDL events.
+   capturing DDL events or transaction commits.
   </para>
 
   <para>
@@ -29,8 +29,9 @@
      occurs in the database in which it is defined. Currently, the only
      supported events are
      <literal>ddl_command_start</>,
-     <literal>ddl_command_end</>
-     and <literal>sql_drop</>.
+     <literal>ddl_command_end</>,
+     <literal>sql_drop</>, and
+     <literal>transaction_commit</>.
      Support for additional events may be added in future releases.
    </para>
 
@@ -65,6 +66,15 @@
    </para>
 
    <para>
+    A <literal>transaction_commit</> trigger is called at the end of a
+    transaction, just before any deferred triggers are fired, unless
+    no data changes have been made by the transaction, or
+    <productname>PostgreSQL</> is running in Single-User mode. This is so
+    that you can recover from a badly specified <literal>transaction_commit</>
+    trigger.
+   </para>
+
+   <para>
      Event triggers (like other functions) cannot be executed in an aborted
      transaction.  Thus, if a DDL command fails with an error, any associated
      <literal>ddl_command_end</> triggers will not be executed.  Conversely,
@@ -77,8 +87,13 @@
    </para>
 
    <para>
-     For a complete list of commands supported by the event trigger mechanism,
-     see <xref linkend="event-trigger-matrix">.
+    A <literal>transaction_commit</> trigger is also not called in an
+    aborted transaction.
+   </para>
+
+   <para>
+     For a complete list of commands supported by the event trigger
+     mechanism, see <xref linkend="event-trigger-matrix">.
    </para>
 
    <para>
@@ -101,6 +116,11 @@
      to intercept. A common use of such triggers is to restrict the range of
      DDL operations which users may perform.
    </para>
+
+   <para>
+    <literal>transaction_commit</> triggers do not currently support
+    <literal>WHEN</literal> clauses.
+   </para>
   </sect1>
 
   <sect1 id="event-trigger-matrix">
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index fbe9543..87a9dab 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -8,39 +8,108 @@
  </indexterm>
 
  <para>
-  This module implements the <type>hstore</> data type for storing sets of
-  key/value pairs within a single <productname>PostgreSQL</> value.
+  This module implements the <type>hstore</> data type for storing arbitrarily
+  nested key/value pairs and arrays 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 and values are
-  simply text strings.
+  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.
  </para>
 
  <sect2>
   <title><type>hstore</> External Representation</title>
 
   <para>
-
    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:
+   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:
 
-<synopsis>
-k =&gt; v
-foo =&gt; bar, baz =&gt; whatever
-"1-a" =&gt; "anything at all"
-</synopsis>
+<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>
 
    The order of the pairs is not significant (and may not be reproduced on
-   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.
+   output).
   </para>
 
   <para>
-   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:
+   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:
 
 <programlisting>
 SELECT 'a=&gt;1,a=&gt;2'::hstore;
@@ -51,14 +120,58 @@ SELECT 'a=&gt;1,a=&gt;2'::hstore;
   </para>
 
   <para>
-   A value (but not a key) can be an SQL <literal>NULL</>. For example:
+   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>:
 
 <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>.
+=% 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>
   </para>
 
   <note>
@@ -83,6 +196,36 @@ key =&gt; NULL
  </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>
 
   <para>
@@ -98,6 +241,7 @@ key =&gt; NULL
     <thead>
      <row>
       <entry>Operator</entry>
+      <entry>Returns</entry>
       <entry>Description</entry>
       <entry>Example</entry>
       <entry>Result</entry>
@@ -107,13 +251,103 @@ key =&gt; NULL
     <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>t</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>f</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>t</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>
@@ -121,6 +355,7 @@ key =&gt; NULL
 
      <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>
@@ -128,13 +363,31 @@ key =&gt; NULL
 
      <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>t</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>t</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>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>t</literal></entry>
@@ -142,6 +395,7 @@ key =&gt; NULL
 
      <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>t</literal></entry>
@@ -149,6 +403,7 @@ key =&gt; NULL
 
      <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>t</literal></entry>
@@ -156,6 +411,7 @@ key =&gt; NULL
 
      <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>f</literal></entry>
@@ -163,13 +419,23 @@ key =&gt; NULL
 
      <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>
+     </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>
@@ -177,13 +443,23 @@ key =&gt; NULL
 
      <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>
+     </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>
@@ -191,6 +467,7 @@ key =&gt; NULL
 
      <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>
@@ -198,6 +475,7 @@ key =&gt; NULL
 
      <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>
@@ -208,12 +486,22 @@ key =&gt; NULL
   </table>
 
   <note>
-  <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>
+    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>
   </note>
 
@@ -258,6 +546,14 @@ key =&gt; NULL
      </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>
       <entry>make single-item <type>hstore</></entry>
@@ -266,6 +562,54 @@ key =&gt; NULL
      </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>
       <entry>get <type>hstore</>'s keys as an array</entry>
@@ -306,6 +650,18 @@ b
      </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>
       <entry>get <type>hstore</>'s keys and values as an array of alternating
@@ -339,6 +695,14 @@ b
      </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>
+     </row>
+
+     <row>
       <entry><function>slice(hstore, text[])</function><indexterm><primary>slice</primary></indexterm></entry>
       <entry><type>hstore</type></entry>
       <entry>extract a subset of an <type>hstore</></entry>
@@ -361,6 +725,20 @@ b
      </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>
       <entry>does <type>hstore</> contain key?</entry>
@@ -377,6 +755,30 @@ b
      </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;}</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>
@@ -404,7 +806,15 @@ b
       <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 Examples section</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></entry>
      </row>
 
@@ -415,7 +825,8 @@ b
   <note>
    <para>
      The function <function>hstore_to_json</function> is used when an <type>hstore</type>
-     value is cast to <type>json</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>.
    </para>
   </note>
 
@@ -426,6 +837,14 @@ 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>
@@ -458,6 +877,155 @@ CREATE INDEX hidx ON testhstore USING HASH (h);
  </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>
 
   <para>
@@ -489,20 +1057,6 @@ SELECT hstore(t) FROM test AS t;
   </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>
-
-  <para>
    Modify an existing record using the values from an <type>hstore</>:
 <programlisting>
 CREATE TABLE test (col1 integer, col2 text, col3 text);
@@ -567,11 +1121,14 @@ SELECT key, count(*) FROM
  <sect2>
   <title>Compatibility</title>
 
-  <para>
-   As of PostgreSQL 9.0, <type>hstore</> uses a different internal
-   representation than previous versions. This presents no obstacle for
+  <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
    dump/restore upgrades since the text representation (used in the dump) is
-   unchanged.
+   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.
   </para>
 
   <para>
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 356890d..f41d6fc 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -203,6 +203,7 @@ distprep:
 	$(MAKE) -C catalog	schemapg.h postgres.bki postgres.description postgres.shdescription
 	$(MAKE) -C replication	repl_gram.c repl_scanner.c
 	$(MAKE) -C utils	fmgrtab.c fmgroids.h errcodes.h
+	$(MAKE) -C utils/adt	jsonb_gram.c jsonb_scan.c
 	$(MAKE) -C utils/misc	guc-file.c
 	$(MAKE) -C utils/sort	qsort_tuple.c
 
@@ -320,6 +321,9 @@ maintainer-clean: distclean
 	      utils/fmgroids.h \
 	      utils/fmgrtab.c \
 	      utils/errcodes.h \
+	      utils/adt/jsonb_gram.c \
+	      utils/adt/jsonb_gram.h \
+	      utils/adt/jsonb_scan.c \
 	      utils/misc/guc-file.c \
 	      utils/sort/qsort_tuple.c
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 0487be1..912cf6a 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -30,6 +30,7 @@
 #include "catalog/namespace.h"
 #include "catalog/storage.h"
 #include "commands/async.h"
+#include "commands/event_trigger.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
 #include "executor/spi.h"
@@ -1866,6 +1867,16 @@ CommitTransaction(void)
 	Assert(s->parent == NULL);
 
 	/*
+	 * First fire any pre-commit triggers, so if they in turn cause any
+	 * deferred triggers etc to fire this will be picked up below.
+	 * Only fire them, though, if we have a real transaction ID and
+	 * we're not running standalone. Not firing when standalone provides
+	 * a way to recover from setting up a bad transaction trigger.
+	 */
+	if (s->transactionId != InvalidTransactionId && IsUnderPostmaster)
+		PreCommitTriggersFire();
+
+	/*
 	 * Do pre-commit processing that involves calling user-defined code, such
 	 * as triggers.  Since closing cursors could queue trigger actions,
 	 * triggers could open cursors, etc, we have to keep looping until there's
@@ -2071,6 +2082,16 @@ PrepareTransaction(void)
 	Assert(s->parent == NULL);
 
 	/*
+	 * First fire any pre-commit triggers, so if they in turn cause any
+	 * deferred triggers etc to fire this will be picked up below.
+	 * Only fire them, though, if we have a real transaction ID and
+	 * we're not running standalone. Not firing when standalone provides
+	 * a way to recover from setting up a bad transaction trigger.
+	 */
+	if (s->transactionId != InvalidTransactionId && IsUnderPostmaster)
+		PreCommitTriggersFire();
+
+	/*
 	 * Do pre-commit processing that involves calling user-defined code, such
 	 * as triggers.  Since closing cursors could queue trigger actions,
 	 * triggers could open cursors, etc, we have to keep looping until there's
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 043d118..ca6d14c 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -796,3 +796,11 @@ CREATE OR REPLACE FUNCTION
 CREATE OR REPLACE FUNCTION
   json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
   RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'json_populate_recordset';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_record(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS anyelement LANGUAGE internal STABLE AS 'jsonb_populate_record';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_recordset(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'jsonb_populate_recordset';
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 59f0842..2d6eb56 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -153,7 +153,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 	/* Validate event name. */
 	if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
 		strcmp(stmt->eventname, "ddl_command_end") != 0 &&
-		strcmp(stmt->eventname, "sql_drop") != 0)
+		strcmp(stmt->eventname, "sql_drop") != 0 &&
+		strcmp(stmt->eventname, "transaction_commit") != 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("unrecognized event name \"%s\"",
@@ -1294,3 +1295,42 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
 
 	return (Datum) 0;
 }
+
+/*
+ * PreCommitTriggersFire
+ *
+ * fire triggers set for the commit event.
+ *
+ * This will be called just before deferred RI and Constraint triggers are
+ * fired.
+ */
+void
+PreCommitTriggersFire(void)
+{
+	List * trigger_list;
+	EventTriggerData trigdata;
+	List	   *runlist = NIL;
+	ListCell   *lc;
+
+	trigger_list = EventCacheLookup(EVT_Commit);
+
+	foreach(lc, trigger_list)
+	{
+		EventTriggerCacheItem *item = lfirst(lc);
+
+		runlist = lappend_oid(runlist, item->fnoid);
+	}
+
+	/* don't spend any more time on this if no functions to run */
+	if (runlist == NIL)
+		return;
+
+	trigdata.type = T_EventTriggerData;
+	trigdata.event = "transaction_commit";
+	trigdata.parsetree = NULL;
+	trigdata.tag = "COMMIT";
+
+	EventTriggerInvoke(runlist, &trigdata);
+
+	list_free(runlist);
+}
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
 	tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
 	tsvector.o tsvector_op.o tsvector_parser.o \
 	txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-	rangetypes_typanalyze.o rangetypes_selfuncs.o
+	rangetypes_typanalyze.o rangetypes_selfuncs.o \
+	jsonb.o jsonb_support.o
 
 like.o: like.c like_match.c
 
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 21a2336..46f4504 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -1804,12 +1804,15 @@ escape_json(StringInfo buf, const char *str)
 Datum
 json_typeof(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonTokenType tok;
 	char *type;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
+
 	/* Lex exactly one token from the input and check its type. */
 	json_lex(lex);
 	tok = lex_peek(lex);
@@ -1840,3 +1843,4 @@ json_typeof(PG_FUNCTION_ARGS)
 
 	PG_RETURN_TEXT_P(cstring_to_text(type));
 }
+
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
new file mode 100644
index 0000000..2b34a93
--- /dev/null
+++ b/src/backend/utils/adt/jsonb.c
@@ -0,0 +1,565 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.c
+ *		I/O for jsonb type 
+ *
+ * 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
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/json.h"
+#include "utils/jsonapi.h"
+#include "utils/jsonb.h"
+
+static size_t
+checkStringLen(size_t len)
+{
+	 if (len > JSONB_MAX_STRING_LEN)
+		  ereport(ERROR,
+					 (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+					  errmsg("string too long for jsonb string")));
+	 return len;
+}
+
+static Jsonb*
+dumpJsonb(JsonbValue *p)
+{
+	 uint32			 buflen;
+	 Jsonb			*out;
+
+	 if (p == NULL)
+	 {
+		  buflen = 0;
+		  out = palloc(VARHDRSZ);
+	 }
+	 else
+	 {
+		  buflen = VARHDRSZ + p->size;
+		  out = palloc(buflen);
+		  SET_VARSIZE(out, buflen);
+
+		  buflen = compressJsonb(p, VARDATA(out));
+	 }
+	 SET_VARSIZE(out, buflen + VARHDRSZ);
+
+	 return out;
+}
+
+typedef struct JsonbInState
+{
+	ToJsonbState	*state;
+	JsonbValue		*res;
+}	JsonbInState;
+
+
+/*
+ * for jsonb we always want the de-escaped value - that's what's in token 
+ */
+
+static void 
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+	JsonbValue		v;
+
+	v.size = sizeof(JEntry);
+
+	switch (tokentype)
+	{
+			
+	case JSON_TOKEN_STRING:
+		v.type = jbvString;
+		v.string.len = token ? checkStringLen(strlen(token)) : 0;
+		v.string.val = token ? pnstrdup(token, v.string.len) : NULL;
+		v.size += v.string.len;
+		break;
+	case JSON_TOKEN_NUMBER:
+		v.type = jbvNumeric;
+		v.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
+		v.size += VARSIZE_ANY(v.numeric) + sizeof(JEntry) /* alignment */;
+		break;
+	case JSON_TOKEN_TRUE:
+		v.type = jbvBool;
+		v.boolean = true;
+		break;
+	case JSON_TOKEN_FALSE:
+		v.type = jbvBool;
+		v.boolean = false;
+		break;
+	case JSON_TOKEN_NULL:
+		v.type = jbvNull;
+		break;
+	default: /* nothing else should be here in fact */
+		break;
+	}
+
+	if (_state->state == NULL)
+	{
+		/* single scalar */
+		JsonbValue	va;
+
+		va.type = jbvArray;
+		va.array.scalar = true;
+		va.array.nelems = 1;
+
+		_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, &va);
+		_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+		_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+	}
+	else
+	{
+		JsonbValue	*o = &_state->state->v;
+
+		switch(o->type)
+		{
+			case jbvArray:
+				_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+				break;
+			case jbvHash:
+				_state->res = pushJsonbValue(&_state->state, WJB_VALUE, &v);
+				break;
+			default:
+				elog(ERROR, "Wrong state");
+		}
+	}
+}
+
+static void
+jsonb_in_object_start(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_OBJECT, NULL);
+}
+
+static void
+jsonb_in_object_end(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_OBJECT, NULL);
+}
+
+static void
+jsonb_in_array_start(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, NULL);
+}
+
+static void
+jsonb_in_array_end(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+}
+
+static void
+jsonb_in_object_field_start(void *state, char *fname, bool isnull)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+	JsonbValue		v;
+
+	v.type = jbvString;
+	v.string.len = fname ? checkStringLen(strlen(fname)) : 0;
+	v.string.val = fname ? pnstrdup(fname, v.string.len) : NULL;
+	v.size = sizeof(JEntry) + v.string.len;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_KEY, &v);
+}
+
+Datum
+jsonb_in(PG_FUNCTION_ARGS)
+{
+	char	   		*json = PG_GETARG_CSTRING(0);
+	text	   		*result = cstring_to_text(json);
+	JsonLexContext 	*lex;
+	JsonbInState 	state;
+	JsonSemAction 	sem;
+
+	memset(&state, 0, sizeof(state));
+	memset(&sem, 0, sizeof(sem));
+	lex = makeJsonLexContext(result, true);
+
+	sem.semstate = (void *) &state;
+
+	sem.object_start = jsonb_in_object_start;
+	sem.array_start = jsonb_in_array_start;
+	sem.object_end = jsonb_in_object_end;
+	sem.array_end = jsonb_in_array_end;
+	sem.scalar = jsonb_in_scalar;
+	sem.object_field_start = jsonb_in_object_field_start;
+
+	pg_parse_json(lex, &sem);
+
+	/* after parsing, the item membar has the composed jsonn structure */
+	PG_RETURN_POINTER(dumpJsonb(state.res));
+}
+
+static void recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header);
+
+static void
+recvJsonbValue(StringInfo buf, JsonbValue *v, uint32 level, int c)
+{
+	 uint32  hentry = c & JENTRY_TYPEMASK;
+
+	 if (hentry == JENTRY_ISNULL)
+	 {
+		  v->type = jbvNull;
+		  v->size = sizeof(JEntry);
+	 }
+	 else if (hentry == JENTRY_ISOBJECT || hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	 {
+		  recvJsonb(buf, v, level + 1, (uint32)c);
+	 }
+	 else if (hentry == JENTRY_ISFALSE || hentry == JENTRY_ISTRUE)
+	 {
+		  v->type = jbvBool;
+		  v->size = sizeof(JEntry);
+		  v->boolean = (hentry == JENTRY_ISFALSE) ? false : true;
+	 }
+	 else if (hentry == JENTRY_ISNUMERIC)
+	 {
+		  v->type = jbvNumeric;
+		  v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv, PointerGetDatum(buf),
+																			Int32GetDatum(0), Int32GetDatum(-1)));
+		  v->size = sizeof(JEntry) * 2 + VARSIZE_ANY(v->numeric);
+	 }
+	 else if (hentry == JENTRY_ISSTRING)
+	 {
+		  v->type = jbvString;
+		  v->string.val = pq_getmsgtext(buf, c, &c);
+		  v->string.len = checkStringLen(c);
+		  v->size = sizeof(JEntry) + v->string.len;
+	 }
+	 else
+	 {
+		  elog(ERROR, "bogus input");
+	 }
+}
+
+static void
+recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header)
+{
+	 uint32  hentry;
+	 uint32  i;
+
+	 hentry = header & JENTRY_TYPEMASK;
+
+	 v->size = 3 * sizeof(JEntry);
+	 if (hentry == JENTRY_ISOBJECT)
+	 {
+		  v->type = jbvHash;
+		  v->hash.npairs = header & JB_COUNT_MASK;
+		  if (v->hash.npairs > 0)
+		  {
+				v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+				for(i=0; i<v->hash.npairs; i++)
+				{
+					 recvJsonbValue(buf, &v->hash.pairs[i].key, level, pq_getmsgint(buf, 4));
+					 if (v->hash.pairs[i].key.type != jbvString)
+						  elog(ERROR, "jsonb's key could be only a string");
+
+					 recvJsonbValue(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;
+				}
+
+				uniqueJsonbValue(v);
+		  }
+	 }
+	 else if (hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	 {
+		  v->type = jbvArray;
+		  v->array.nelems = header & JB_COUNT_MASK;
+		  v->array.scalar = (hentry == JENTRY_ISCALAR) ? true : false;
+
+		  if (v->array.scalar && v->array.nelems != 1)
+				elog(ERROR, "bogus input");
+
+		  if (v->array.nelems > 0)
+		  {
+				v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+				for(i=0; i<v->array.nelems; i++)
+				{
+					 recvJsonbValue(buf, v->array.elems + i, level, pq_getmsgint(buf, 4));
+					 v->size += v->array.elems[i].size;
+				}
+		  }
+	 }
+	 else
+	 {
+				elog(ERROR, "bogus input");
+	 }
+}
+
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	 StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
+	 JsonbValue v;
+
+	 recvJsonb(buf, &v, 0, pq_getmsgint(buf, 4));
+
+	 PG_RETURN_POINTER(dumpJsonb(&v));
+}
+
+static void
+putEscapedValue(StringInfo out, JsonbValue *v)
+{
+	 switch(v->type)
+	 {
+		  case jbvNull:
+				appendBinaryStringInfo(out, "null", 4);
+				break;
+		  case jbvString:
+				escape_json(out, pnstrdup(v->string.val, v->string.len));
+				break;
+		  case jbvBool:
+				if (v->boolean)
+					 appendBinaryStringInfo(out, "true", 4);
+				else
+					 appendBinaryStringInfo(out, "false", 5);
+				break;
+		  case jbvNumeric:
+				appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+				break;
+		  default:
+				elog(PANIC, "Unknown type");
+	 }
+}
+
+char*
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{
+	 bool			first = true;
+	 JsonbIterator  *it;
+	 int			type;
+	 JsonbValue	  	v;
+	 int			 level = 0;
+
+	 if (out == NULL)
+		  out = makeStringInfo();
+
+	 if (in == NULL)
+	 {
+		  appendStringInfoString(out, "");
+		  return out->data;
+	 }
+
+	 enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);
+
+	 it = JsonbIteratorInit(in);
+
+	 while((type = JsonbIteratorGet(&it, &v, false)) != 0)
+	 {
+reout:
+		  switch(type)
+		  {
+				case WJB_BEGIN_ARRAY:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+
+					 if (v.array.scalar == false)
+						 appendStringInfoChar(out, '[');
+					 level++;
+					 break;
+				case WJB_BEGIN_OBJECT:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+					 appendStringInfoCharMacro(out, '{');
+
+					 level++;
+					 break;
+				case WJB_KEY:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+
+					 putEscapedValue(out, &v);
+					 appendBinaryStringInfo(out, ": ", 2);
+
+					 type = JsonbIteratorGet(&it, &v, false);
+					 if (type == WJB_VALUE)
+					 {
+						  first = false;
+						  putEscapedValue(out, &v);
+					 }
+					 else
+					 {
+						  Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+						  goto reout;
+					 }
+					 break;
+				case WJB_ELEM:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 else
+						  first = false;
+
+					 putEscapedValue(out, &v);
+					 break;
+				case WJB_END_ARRAY:
+					 level--;
+					 if (v.array.scalar == false)
+						  appendStringInfoChar(out, ']');
+					 first = false;
+					 break;
+				case WJB_END_OBJECT:
+					 level--;
+					 appendStringInfoCharMacro(out, '}');
+					 first = false;
+					 break;
+				default:
+					 elog(PANIC, "Wrong flags");
+		  }
+	 }
+
+	 Assert(level == 0);
+
+	 return out->data;
+}
+
+Datum
+jsonb_out(PG_FUNCTION_ARGS)
+{
+	 Jsonb  *jb = PG_GETARG_JSONB(0);
+	 char	 *out;
+
+	 out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	 PG_RETURN_CSTRING(out);
+}
+
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	 Jsonb			 *in = PG_GETARG_JSONB(0);
+	 StringInfoData  buf;
+
+	 pq_begintypsend(&buf);
+
+	 if (JB_ISEMPTY(in))
+	 {
+		  pq_sendint(&buf, 0, 4);
+	 }
+	 else
+	 {
+		  JsonbIterator  *it;
+		  int				 type;
+		  JsonbValue	  v;
+		  uint32			 flag;
+		  bytea			  *nbuf;
+
+		  enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */);
+
+		  it = JsonbIteratorInit(VARDATA_ANY(in));
+
+		  while((type = JsonbIteratorGet(&it, &v, false)) != 0)
+		  {
+				switch(type)
+				{
+					 case WJB_BEGIN_ARRAY:
+						  flag = (v.array.scalar) ? JENTRY_ISCALAR : JENTRY_ISARRAY;
+						  pq_sendint(&buf, v.array.nelems | flag, 4);
+						  break;
+					 case WJB_BEGIN_OBJECT:
+						  pq_sendint(&buf, v.hash.npairs | JENTRY_ISOBJECT, 4);
+						  break;
+					 case WJB_KEY:
+						  pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+						  pq_sendtext(&buf, v.string.val, v.string.len);
+						  break;
+					 case WJB_ELEM:
+					 case WJB_VALUE:
+						  switch(v.type)
+						  {
+								case jbvNull:
+									 pq_sendint(&buf, JENTRY_ISNULL, 4);
+									 break;
+								case jbvString:
+									 pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+									 pq_sendtext(&buf, v.string.val, v.string.len);
+									 break;
+								case jbvBool:
+									 pq_sendint(&buf, (v.boolean) ? JENTRY_ISTRUE : JENTRY_ISFALSE, 4);
+									 break;
+								case jbvNumeric:
+									 nbuf = DatumGetByteaP(DirectFunctionCall1(numeric_send, NumericGetDatum(v.numeric)));
+									 pq_sendint(&buf, VARSIZE_ANY(nbuf) | JENTRY_ISNUMERIC, 4);
+									 pq_sendbytes(&buf, (char*)nbuf, VARSIZE_ANY(nbuf));
+									 break;
+								default:
+									 elog(PANIC, "Wrong type: %u", v.type);
+						  }
+						  break;
+					 case WJB_END_ARRAY:
+					 case WJB_END_OBJECT:
+						  break;
+					 default:
+						  elog(PANIC, "Wrong flags");
+				}
+		  }
+	 }
+
+	 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{
+	 Jsonb			 *in = PG_GETARG_JSONB(0);
+	 JsonbIterator   *it;
+	 JsonbValue	      v;
+	 char            *result;
+
+	 if (JB_ROOT_IS_OBJECT(in))
+		 result = "object";
+	 else if (JB_ROOT_IS_ARRAY(in) && ! JB_ROOT_IS_SCALAR(in))
+		 result = "array";
+	 else
+	 {
+		 Assert(JB_ROOT_IS_SCALAR(in));
+
+		 it = JsonbIteratorInit(VARDATA_ANY(in));
+		 /* 
+		  * a root scalar is stored as an array of one element, 
+		  * so we get the array and then its first (and only) member.
+		  */
+		 (void) JsonbIteratorGet(&it, &v, true);
+		 (void) JsonbIteratorGet(&it, &v, true);
+		 switch(v.type)
+		 {
+		 case jbvNull:
+			 result = "null";
+			 break;
+		 case jbvString:
+			 result = "string";
+			 break;
+		 case jbvBool:
+			 result = "boolean";
+			 break;
+		 case jbvNumeric:
+			 result = "number";
+			 break;
+		 default:
+			 elog(ERROR, "Wrong jsonb scalar type: %u", v.type);
+		 }
+	 }
+	 
+	PG_RETURN_TEXT_P(cstring_to_text(result));		 
+}
diff --git a/src/backend/utils/adt/jsonb_support.c b/src/backend/utils/adt/jsonb_support.c
new file mode 100644
index 0000000..e23f8b9
--- /dev/null
+++ b/src/backend/utils/adt/jsonb_support.c
@@ -0,0 +1,1217 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb_support.c
+ *    Support functions for jsonb
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/adt/jsonb_support.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "utils/builtins.h"
+#include "utils/jsonb.h"
+
+/*
+ * Sort and unique pairs in hash-like JsonbValue
+ */
+void
+uniqueJsonbValue(JsonbValue *v)
+{
+	bool    hasNonUniq = false;
+
+	Assert(v->type == jbvHash);
+
+	if (v->hash.npairs > 1)
+		qsort_arg(v->hash.pairs, v->hash.npairs, sizeof(*v->hash.pairs),
+				  compareJsonbPair, &hasNonUniq);
+
+	if (hasNonUniq)
+	{
+		JsonbPair	*ptr = v->hash.pairs + 1,
+					*res = v->hash.pairs;
+
+		while(ptr - v->hash.pairs < v->hash.npairs)
+		{
+			if (ptr->key.string.len == res->key.string.len &&
+				memcmp(ptr->key.string.val, res->key.string.val,
+				ptr->key.string.len) == 0)
+			{
+				v->size -= ptr->key.size + ptr->value.size;
+			}
+			else
+			{
+				res++;
+				if (ptr != res)
+					memcpy(res, ptr, sizeof(*res));
+			}
+			ptr++;
+		}
+
+		v->hash.npairs = res + 1 - v->hash.pairs;
+	}
+}
+
+/****************************************************************************
+ *                         Compare Functions                                *
+ ****************************************************************************/
+int
+compareJsonbStringValue(const void *a, const void *b, void *arg)
+{
+	const JsonbValue  *va = a;
+	const JsonbValue  *vb = b;
+	int					res;
+
+	Assert(va->type == jbvString);
+	Assert(vb->type == jbvString);
+
+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool*)arg = true;
+	}
+	else
+	{
+		res = (va->string.len > vb->string.len) ? 1 : -1;
+	}
+
+	return res;
+}
+
+int
+compareJsonbPair(const void *a, const void *b, void *arg)
+{
+	const 	JsonbPair *pa = a;
+	const 	JsonbPair *pb = b;
+	int 	res;
+
+	res = compareJsonbStringValue(&pa->key, &pb->key, arg);
+
+	/*
+	 * guarantee keeping order of equal pair. Unique algorithm will
+	 * prefer first element as value
+	 */
+
+	if (res == 0)
+		res = (pa->order > pb->order) ? -1 : 1;
+
+	return res;
+}
+
+int
+compareJsonbValue(JsonbValue *a, JsonbValue *b)
+{
+	if (a->type == b->type)
+	{
+		switch(a->type)
+		{
+			case jbvNull:
+				return 0;
+			case jbvString:
+				return compareJsonbStringValue(a, b, NULL);
+			case jbvBool:
+				if (a->boolean == b->boolean)
+					return 0;
+				return (a->boolean > b->boolean) ? 1 : -1;
+			case jbvNumeric:
+				return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+														 PointerGetDatum(a->numeric),
+														 PointerGetDatum(b->numeric)));
+			case jbvArray:
+				if (a->array.nelems == b->array.nelems)
+				{
+					int i, r;
+
+					for(i=0; i<a->array.nelems; i++)
+						if ((r = compareJsonbValue(a->array.elems + i, 
+												   b->array.elems + i)) != 0)
+							return r;
+
+					return 0;
+				}
+
+				return (a->array.nelems > b->array.nelems) ? 1 : -1;
+			case jbvHash:
+				if (a->hash.npairs == b->hash.npairs)
+				{
+					int i, r;
+
+					for(i=0; i<a->hash.npairs; i++)
+					{
+						if ((r = compareJsonbStringValue(&a->hash.pairs[i].key,
+														 &b->hash.pairs[i].key,
+														 NULL)) != 0)
+							return r;
+						if ((r = compareJsonbValue(&a->hash.pairs[i].value, 
+												   &b->hash.pairs[i].value)) != 0)
+							return r;
+					}
+
+					return 0;
+				}
+
+				return (a->hash.npairs > b->hash.npairs) ? 1 : -1;
+			case jbvBinary:
+				return compareJsonbBinaryValue(a->binary.data, b->binary.data);
+			default:
+				elog(PANIC, "unknown JsonbValue->type: %d", a->type);
+		}
+	}
+
+	return (a->type > b->type) ? 1 : -1;
+}
+
+int
+compareJsonbBinaryValue(char *a, char *b)
+{
+	JsonbIterator	*it1, *it2;
+	int				res = 0;
+
+	it1 = JsonbIteratorInit(a);
+	it2 = JsonbIteratorInit(b);
+
+	while(res == 0)
+	{
+		JsonbValue		v1, v2;
+		int				r1, r2;
+
+		r1 = JsonbIteratorGet(&it1, &v1, false);
+		r2 = JsonbIteratorGet(&it2, &v2, false);
+
+		if (r1 == r2)
+		{
+			if (r1 == 0)
+				break; /* equal */
+
+			if (v1.type == v2.type)
+			{
+				switch(v1.type)
+				{
+					case jbvString:
+						res = compareJsonbStringValue(&v1, &v2, NULL);
+						break;
+					case jbvBool:
+						if (v1.boolean == v2.boolean)
+							res = 0;
+						else
+							res = (v1.boolean > v2.boolean) ? 1 : -1;
+						break;
+					case jbvNumeric:
+						res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+												 PointerGetDatum(v1.numeric),
+												 PointerGetDatum(v2.numeric)));
+						break;
+					case jbvArray:
+						if (v1.array.nelems != v2.array.nelems)
+							res = (v1.array.nelems > v2.array.nelems) ? 1 : -1;
+						break;
+					case jbvHash:
+						if (v1.hash.npairs != v2.hash.npairs)
+							res = (v1.hash.npairs > v2.hash.npairs) ? 1 : -1;
+						break;
+					default:
+						break;
+				}
+			}
+			else
+			{
+				res = (v1.type > v2.type) ?  1 : -1; /* dummy order */
+			}
+		}
+		else
+		{
+			res = (r1 > r2) ? 1 : -1; /* dummy order */
+		}
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *                         find string key in hash or array                 *
+ ****************************************************************************/
+JsonbValue*
+findUncompressedJsonbValueByValue(char *buffer, uint32 flags, uint32 *lowbound, JsonbValue *key)
+{
+	uint32				header = *(uint32*)buffer;
+	static JsonbValue 	r;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) != 
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		JEntry	*array = (JEntry*)(buffer + sizeof(header));
+		char 	*data = (char*)(array + (header & JB_COUNT_MASK));
+		int 	i;
+
+		for(i=(lowbound) ? *lowbound : 0; i<(header & JB_COUNT_MASK); i++) {
+			JEntry	*e = array + i;
+
+			if (JBE_ISNULL(*e) && key->type == jbvNull)
+			{
+				r.type = jbvNull;
+				if (lowbound)
+					*lowbound = i;
+				r.size = sizeof(JEntry);
+
+				return &r;
+			}
+			else if (JBE_ISSTRING(*e) && key->type == jbvString )
+			{
+				if (key->string.len == JBE_LEN(*e) &&
+					memcmp(key->string.val, data + JBE_OFF(*e), 
+						   key->string.len) == 0)
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*e);
+					r.string.len = key->string.len;
+					r.size = sizeof(JEntry) + r.string.len;
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISBOOL(*e) && key->type == jbvBool)
+			{
+				if ((JBE_ISBOOL_TRUE(*e) && key->boolean == true) ||
+					(JBE_ISBOOL_FALSE(*e) && key->boolean == false))
+				{
+					r = *key;
+					r.size = sizeof(JEntry);
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISNUMERIC(*e) && key->type == jbvNumeric)
+			{
+				if (DatumGetBool(DirectFunctionCall2(numeric_eq, 
+								 PointerGetDatum(data + INTALIGN(JBE_OFF(*e))),
+								 PointerGetDatum(key->numeric))) == true)
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*e)));
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+		}
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		JEntry  *array = (JEntry*)(buffer + sizeof(header));
+		char    *data = (char*)(array + (header & JB_COUNT_MASK) * 2);
+		uint32	stopLow = lowbound ? *lowbound : 0,
+				stopHigh = (header & JB_COUNT_MASK),
+				stopMiddle;
+
+		if (key->type != jbvString)
+			return NULL;
+
+		while (stopLow < stopHigh)
+		{
+			int		difference;
+			JEntry	*e;
+
+			stopMiddle = stopLow + (stopHigh - stopLow) / 2;
+
+			e = array + stopMiddle * 2;
+
+			if (key->string.len == JBE_LEN(*e))
+				difference = memcmp(data + JBE_OFF(*e), key->string.val,
+									key->string.len);
+			else
+				difference = (JBE_LEN(*e) > key->string.len) ? 1 : -1;
+
+			if (difference == 0)
+			{
+				JEntry	*v = e + 1;
+
+				if (lowbound)
+					*lowbound = stopMiddle + 1;
+
+				if (JBE_ISSTRING(*v))
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*v);
+					r.string.len = JBE_LEN(*v);
+					r.size = sizeof(JEntry) + r.string.len;
+				}
+				else if (JBE_ISBOOL(*v))
+				{
+					r.type = jbvBool;
+					r.boolean = (JBE_ISBOOL_TRUE(*v)) ? true : false;
+					r.size = sizeof(JEntry);
+				}
+				else if (JBE_ISNUMERIC(*v))
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*v)));
+					r.size = 2*sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+				}
+				else if (JBE_ISNULL(*v))
+				{
+					r.type = jbvNull;
+					r.size = sizeof(JEntry);
+				}
+				else
+				{
+					r.type = jbvBinary;
+					r.binary.data = data + INTALIGN(JBE_OFF(*v));
+					r.binary.len = JBE_LEN(*v) - 
+									(INTALIGN(JBE_OFF(*v)) - JBE_OFF(*v));
+					r.size = 2*sizeof(JEntry) + r.binary.len;
+				}
+
+				return &r;
+			}
+			else if (difference < 0)
+			{
+				stopLow = stopMiddle + 1;
+			}
+			else
+			{
+				stopHigh = stopMiddle;
+			}
+		}
+
+		if (lowbound)
+			*lowbound = stopLow;
+	}
+
+	return NULL;
+}
+
+JsonbValue*
+findUncompressedJsonbValue(char *buffer, uint32 flags, uint32 *lowbound,
+						   char *key, uint32 keylen)
+{
+	JsonbValue	v;
+
+	if (key == NULL)
+	{
+		v.type = jbvNull;
+	}
+	else
+	{
+		v.type = jbvString;
+		v.string.val = key;
+		v.string.len = keylen;
+	}
+
+	return findUncompressedJsonbValueByValue(buffer, flags, lowbound, &v);
+}
+
+JsonbValue*
+getJsonbValue(char *buffer, uint32 flags, int32 i)
+{
+	uint32				header = *(uint32*)buffer;
+	static JsonbValue	r;
+	JEntry				*array, *e;
+	char				*data;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) !=
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (i >= 0)
+	{
+		if (i >= (header & JB_COUNT_MASK))
+			return NULL;
+	}
+	else
+	{
+		if (-i > (header & JB_COUNT_MASK))
+			return NULL;
+
+		i = (header & JB_COUNT_MASK) + i;
+	}
+
+	array = (JEntry*)(buffer + sizeof(header));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		e = array + i;
+		data = (char*)(array + (header & JB_COUNT_MASK));
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		e = array + i * 2 + 1;
+		data = (char*)(array + (header & JB_COUNT_MASK) * 2);
+	}
+	else
+	{
+		return NULL;
+	}
+
+	if (JBE_ISSTRING(*e))
+	{
+		r.type = jbvString;
+		r.string.val = data + JBE_OFF(*e);
+		r.string.len = JBE_LEN(*e);
+		r.size = sizeof(JEntry) + r.string.len;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		r.type = jbvBool;
+		r.boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		r.size = sizeof(JEntry);
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		r.type = jbvNumeric;
+		r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*e)));
+		r.size = 2*sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		r.type = jbvNull;
+		r.size = sizeof(JEntry);
+	}
+	else
+	{
+		r.type = jbvBinary;
+		r.binary.data = data + INTALIGN(JBE_OFF(*e));
+		r.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		r.size = r.binary.len + 2*sizeof(JEntry);
+	}
+
+	return &r;
+}
+
+/****************************************************************************
+ *                         Walk on tree representation of jsonb             *
+ ****************************************************************************/
+static void
+walkUncompressedJsonbDo(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg, uint32 level) 
+{
+	int i;
+
+	switch(v->type)
+	{
+		case jbvArray:
+			cb(cb_arg, v, WJB_BEGIN_ARRAY, level);
+			for(i=0; i<v->array.nelems; i++)
+			{
+				if (v->array.elems[i].type == jbvNull ||
+					v->array.elems[i].type == jbvString ||
+					v->array.elems[i].type == jbvBool ||
+					v->array.elems[i].type == jbvNumeric ||
+					v->array.elems[i].type == jbvBinary)
+					cb(cb_arg, v->array.elems + i, WJB_ELEM, level);
+				else
+					walkUncompressedJsonbDo(v->array.elems + i, cb, cb_arg,
+											level + 1);
+			}
+			cb(cb_arg, v, WJB_END_ARRAY, level);
+			break;
+		case jbvHash:
+			cb(cb_arg, v, WJB_BEGIN_OBJECT, level);
+
+			for(i=0; i<v->hash.npairs; i++)
+			{
+				cb(cb_arg, &v->hash.pairs[i].key, WJB_KEY, level);
+				
+				if (v->hash.pairs[i].value.type == jbvNull ||
+					v->hash.pairs[i].value.type == jbvString ||
+					v->hash.pairs[i].value.type == jbvBool ||
+					v->hash.pairs[i].value.type == jbvNumeric ||
+					v->hash.pairs[i].value.type == jbvBinary)
+					cb(cb_arg, &v->hash.pairs[i].value, WJB_VALUE, level);
+				else 
+					walkUncompressedJsonbDo(&v->hash.pairs[i].value, cb, cb_arg,
+											level + 1);
+			}
+
+			cb(cb_arg, v, WJB_END_OBJECT, level);
+			break;
+		default:
+			elog(PANIC, "impossible JsonbValue->type: %d", v->type);
+	}
+}
+
+void
+walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg)
+{
+	if (v)
+		walkUncompressedJsonbDo(v, cb, cb_arg, 0);
+}
+
+/****************************************************************************
+ *                         Iteration over binary jsonb                      *
+ ****************************************************************************/
+static void
+parseBuffer(JsonbIterator *it, char *buffer)
+{
+	uint32	header = *(uint32*)buffer;
+
+	it->type = header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT);
+	it->nelems = header & JB_COUNT_MASK;
+	it->buffer = buffer;
+
+
+	buffer += sizeof(uint32);
+	it->array = (JEntry*)buffer;
+
+	it->state = jbi_start;
+
+	switch(it->type)
+	{
+		case JB_FLAG_ARRAY:
+			it->data = buffer + it->nelems * sizeof(JEntry);
+			it->isScalar = (header & JB_FLAG_SCALAR) ? true : false;
+			Assert(it->isScalar == false || it->nelems == 1);
+			break;
+		case JB_FLAG_OBJECT:
+			it->data = buffer + it->nelems * sizeof(JEntry) * 2;
+			break;
+		default:
+			elog(PANIC, "impossible type: %08x", it->type);
+	}
+}
+
+JsonbIterator*
+JsonbIteratorInit(char *buffer)
+{
+	JsonbIterator	*it = palloc(sizeof(*it));
+
+	parseBuffer(it, buffer);
+	it->next = NULL;
+
+	return it;
+}
+
+static bool
+formAnswer(JsonbIterator **it, JsonbValue *v, JEntry *e, bool skipNested)
+{
+	if (JBE_ISSTRING(*e))
+	{
+		v->type = jbvString;
+		v->string.val = (*it)->data + JBE_OFF(*e);
+		v->string.len = JBE_LEN(*e);
+		v->size = sizeof(JEntry) + v->string.len;
+
+		return false;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		v->type = jbvBool;
+		v->boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		v->type = jbvNumeric;
+		v->numeric = (Numeric)((*it)->data + INTALIGN(JBE_OFF(*e)));
+		v->size = 2*sizeof(JEntry) + VARSIZE_ANY(v->numeric);
+
+		return false;
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (skipNested)
+	{
+		v->type = jbvBinary;
+		v->binary.data = (*it)->data + INTALIGN(JBE_OFF(*e));
+		v->binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		v->size = v->binary.len + 2*sizeof(JEntry);
+
+		return false;
+	}
+	else
+	{
+		JsonbIterator *nit = palloc(sizeof(*nit));
+
+		parseBuffer(nit, (*it)->data + INTALIGN(JBE_OFF(*e)));
+		nit->next = *it;
+		*it = nit;
+
+		return true;
+	}
+}
+
+static JsonbIterator*
+up(JsonbIterator *it)
+{
+	JsonbIterator *v = it->next;
+
+	pfree(it);
+
+	return v;
+}
+
+int
+JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested)
+{
+	int res;
+
+	if (*it == NULL)
+		return 0;
+
+	/*
+	 * Encode all possible states by one integer. That's possible
+	 * because enum members of JsonbIterator->state uses different bits
+	 * than JB_FLAG_ARRAY/JB_FLAG_OBJECT. See definition of JsonbIterator
+	 */
+
+	switch((*it)->type | (*it)->state)
+	{
+		case JB_FLAG_ARRAY | jbi_start:
+			(*it)->state = jbi_elem;
+			(*it)->i = 0;
+			v->type = jbvArray;
+			v->array.nelems = (*it)->nelems;
+			res = WJB_BEGIN_ARRAY;
+			v->array.scalar = (*it)->isScalar;
+			break;
+		case JB_FLAG_ARRAY | jbi_elem:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_ARRAY;
+			}
+			else if (formAnswer(it, v, &(*it)->array[ (*it)->i++ ], skipNested))
+			{
+				res = JsonbIteratorGet(it, v, skipNested);
+			}
+			else
+			{
+				res = WJB_ELEM;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_start:
+			(*it)->state = jbi_key;
+			(*it)->i = 0;
+			v->type = jbvHash;
+			v->hash.npairs = (*it)->nelems;
+			res = WJB_BEGIN_OBJECT;
+			break;
+		case JB_FLAG_OBJECT | jbi_key:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_OBJECT;
+			}
+			else
+			{
+				formAnswer(it, v, &(*it)->array[ (*it)->i * 2 ], false);
+				(*it)->state = jbi_value;
+				res = WJB_KEY;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_value:
+			(*it)->state = jbi_key;
+			if (formAnswer(it, v, &(*it)->array[ ((*it)->i++) * 2 + 1], skipNested))
+				res = JsonbIteratorGet(it, v, skipNested);
+			else
+				res = WJB_VALUE;
+			break;
+		default:
+			elog(PANIC,"unknown state %08x", (*it)->type & (*it)->state);
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *        Transformation from tree to binary representation of jsonb        *
+ ****************************************************************************/
+typedef struct CompressState
+{
+	char	*begin;
+	char	*ptr;
+
+	struct {
+		uint32	i;
+		uint32	*header;
+		JEntry	*array;
+		char	*begin;
+	} *levelstate, *lptr, *pptr;
+
+	uint32	maxlevel;
+	
+} CompressState;
+
+#define	curLevelState	state->lptr
+#define prevLevelState	state->pptr
+
+static void
+putJEntryString(CompressState *state, JsonbValue* value, uint32 level, uint32 i)
+{
+	curLevelState = state->levelstate + level;
+
+	if (i == 0)
+		curLevelState->array[0].entry = JENTRY_ISFIRST;
+	else
+		curLevelState->array[i].entry = 0;
+
+	switch(value->type)
+	{
+		case jbvNull:
+			curLevelState->array[i].entry |= JENTRY_ISNULL;
+
+			if (i>0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvString:
+			memcpy(state->ptr, value->string.val, value->string.len);
+			state->ptr += value->string.len;
+
+			if (i == 0)
+				curLevelState->array[i].entry |= value->string.len;
+			else
+				curLevelState->array[i].entry |= 
+					(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+					value->string.len;
+			break;
+		case jbvBool:
+			curLevelState->array[i].entry |= (value->boolean) ?
+				JENTRY_ISTRUE : JENTRY_ISFALSE;
+
+			if (i>0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvNumeric:
+			{
+				int addlen = INTALIGN(state->ptr - state->begin) -
+								(state->ptr - state->begin);
+				int	numlen = VARSIZE_ANY(value->numeric); 
+
+				switch(addlen)
+				{
+					case 3:
+						*state->ptr = '\0'; state->ptr++;
+					case 2:
+						*state->ptr = '\0'; state->ptr++;
+					case 1:
+						*state->ptr = '\0'; state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->numeric, numlen);
+				state->ptr += numlen;
+
+				curLevelState->array[i].entry |= JENTRY_ISNUMERIC;
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + numlen;
+				else
+					curLevelState->array[i].entry |= 
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + numlen;
+				break;
+			}
+		case jbvBinary:
+			{
+				int addlen = INTALIGN(state->ptr - state->begin) -
+								(state->ptr - state->begin);
+
+				switch(addlen)
+				{
+					case 3:
+						*state->ptr = '\0'; state->ptr++;
+					case 2:
+						*state->ptr = '\0'; state->ptr++;
+					case 1:
+						*state->ptr = '\0'; state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->binary.data, value->binary.len);
+				state->ptr += value->binary.len;
+
+				curLevelState->array[i].entry |= JENTRY_ISNEST;
+
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + value->binary.len;
+				else
+					curLevelState->array[i].entry |=
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + value->binary.len;
+			}
+			break;
+		default:
+			elog(PANIC,"Unsupported JsonbValue type: %d", value->type);
+	}
+}
+
+static void
+compressCallback(void *arg, JsonbValue* value, uint32 flags, uint32 level)
+{
+	CompressState	*state = arg;
+
+	if (level == state->maxlevel) {
+		state->maxlevel *= 2;
+		state->levelstate = repalloc(state->levelstate,
+								 sizeof(*state->levelstate) * state->maxlevel);
+	}
+
+	curLevelState = state->levelstate + level;
+
+	if (flags & (WJB_BEGIN_ARRAY | WJB_BEGIN_OBJECT))
+	{
+		Assert(((flags & WJB_BEGIN_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_BEGIN_OBJECT) && value->type == jbvHash));
+
+		curLevelState->begin = state->ptr;
+
+		switch(INTALIGN(state->ptr - state->begin) -
+			   (state->ptr - state->begin))
+		{
+			case 3:
+				*state->ptr = '\0'; state->ptr++;
+			case 2:
+				*state->ptr = '\0'; state->ptr++;
+			case 1:
+				*state->ptr = '\0'; state->ptr++;
+			case 0:
+			default:
+				break;
+		}
+
+		curLevelState->header = (uint32*)state->ptr;
+		state->ptr += sizeof(*curLevelState->header);
+
+		curLevelState->array = (JEntry*)state->ptr;
+		curLevelState->i = 0;
+
+		if (value->type == jbvArray)
+		{
+			*curLevelState->header = value->array.nelems | JB_FLAG_ARRAY ;
+			state->ptr += sizeof(JEntry) * value->array.nelems;
+
+			if (value->array.scalar)
+			{
+				Assert(value->array.nelems == 1);
+				Assert(level == 0);
+				*curLevelState->header |= JB_FLAG_SCALAR;
+			}
+		}
+		else
+		{
+			*curLevelState->header = value->hash.npairs | JB_FLAG_OBJECT ;
+			state->ptr += sizeof(JEntry) * value->hash.npairs * 2;
+		}
+	}
+	else if (flags & WJB_ELEM)
+	{
+		putJEntryString(state, value, level, curLevelState->i);
+		curLevelState->i++;
+	}
+	else if (flags & WJB_KEY)
+	{
+		Assert(value->type == jbvString);
+
+		putJEntryString(state, value, level, curLevelState->i * 2);
+	}
+	else if (flags & WJB_VALUE)
+	{
+		putJEntryString(state, value, level, curLevelState->i * 2 + 1);
+		curLevelState->i++;
+	}
+	else if (flags & (WJB_END_ARRAY | WJB_END_OBJECT))
+	{
+		uint32	len, i;
+
+		Assert(((flags & WJB_END_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_END_OBJECT) && value->type == jbvHash));
+		if (level == 0)
+			return;
+
+		len = state->ptr - (char*)curLevelState->begin;
+
+		prevLevelState = curLevelState - 1;
+
+		if (*prevLevelState->header & JB_FLAG_ARRAY) {
+			i = prevLevelState->i;
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			if (i == 0)
+				prevLevelState->array[0].entry |= JENTRY_ISFIRST | len;
+			else
+				prevLevelState->array[i].entry |=
+					(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else if (*prevLevelState->header & JB_FLAG_OBJECT)
+		{
+			i = 2 * prevLevelState->i + 1; /* VALUE, not a KEY */
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			prevLevelState->array[i].entry |=
+				(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else
+		{
+			elog(PANIC, "Wrong parent");
+		}
+
+		Assert(state->ptr - curLevelState->begin <= value->size);
+		prevLevelState->i++;
+	}
+	else
+	{
+		elog(PANIC, "Wrong flags");
+	}
+}
+
+uint32
+compressJsonb(JsonbValue *v, char *buffer) {
+	uint32			l = 0;
+	CompressState	state;
+
+	state.begin = state.ptr = buffer;
+	state.maxlevel = 8;
+	state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel);
+
+	walkUncompressedJsonb(v, compressCallback, &state);
+
+	l = state.ptr - buffer;
+	Assert(l <= v->size);
+
+	return l;
+}
+
+/****************************************************************************
+ *                  Iteration-like forming jsonb                            *
+ *       Note: it believes by default in already sorted keys in hash,       *
+ *     although with r == WJB_END_OBJECT and v == NULL  it will sort itself *
+ ****************************************************************************/
+static ToJsonbState*
+pushState(ToJsonbState **state)
+{
+	ToJsonbState	*ns = palloc(sizeof(*ns));
+
+	ns->next = *state;
+	return ns;
+}
+
+static void
+appendArray(ToJsonbState *state, JsonbValue *v)
+{
+	JsonbValue	*a = &state->v;
+
+	Assert(a->type == jbvArray);
+
+	if (a->array.nelems >= state->size)
+	{
+		state->size *= 2;
+		a->array.elems = repalloc(a->array.elems,
+								   sizeof(*a->array.elems) * state->size);
+	}
+
+	a->array.elems[a->array.nelems ++] = *v;
+
+	a->size += v->size;
+}
+
+static void
+appendKey(ToJsonbState *state, JsonbValue *v)
+{
+	JsonbValue	*h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	if (h->hash.npairs >= state->size)
+	{
+		state->size *= 2;
+		h->hash.pairs = repalloc(h->hash.pairs,
+									sizeof(*h->hash.pairs) * state->size);
+	}
+
+	h->hash.pairs[h->hash.npairs].key = *v;
+	h->hash.pairs[h->hash.npairs].order = h->hash.npairs;
+
+	h->size += v->size;
+}
+
+static void
+appendValue(ToJsonbState *state, JsonbValue *v)
+{
+
+	JsonbValue	*h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	h->hash.pairs[h->hash.npairs++].value = *v;
+
+	h->size += v->size;
+}
+
+
+JsonbValue*
+pushJsonbValue(ToJsonbState **state, int r /* WJB_* */, JsonbValue *v) {
+	JsonbValue	*h = NULL;
+
+	switch(r)
+	{
+		case WJB_BEGIN_ARRAY:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvArray;
+			(*state)->v.size = 3*sizeof(JEntry);
+			(*state)->v.array.nelems = 0;
+			(*state)->v.array.scalar = (v && v->array.scalar) ? true : false;
+			(*state)->size = (v && v->type == jbvArray && v->array.nelems > 0)
+								? v->array.nelems : 4;
+			(*state)->v.array.elems = palloc(sizeof(*(*state)->v.array.elems) *
+											 (*state)->size);
+			break;
+		case WJB_BEGIN_OBJECT:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvHash;
+			(*state)->v.size = 3*sizeof(JEntry);
+			(*state)->v.hash.npairs = 0;
+			(*state)->size = (v && v->type == jbvHash && v->hash.npairs > 0) ?
+									v->hash.npairs : 4;
+			(*state)->v.hash.pairs = palloc(sizeof(*(*state)->v.hash.pairs) *
+											(*state)->size);
+			break;
+		case WJB_ELEM:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric || 
+				   v->type == jbvBinary); 
+			appendArray(*state, v);
+			break;
+		case WJB_KEY:
+			Assert(v->type == jbvString);
+			appendKey(*state, v);
+			break;
+		case WJB_VALUE:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric || 
+				   v->type == jbvBinary); 
+			appendValue(*state, v);
+			break;
+		case WJB_END_OBJECT:
+			h = &(*state)->v;
+			if (v == NULL)
+				uniqueJsonbValue(h);
+		case WJB_END_ARRAY:
+			h = &(*state)->v;
+			*state = (*state)->next;
+			if (*state)
+			{
+				switch((*state)->v.type)
+				{
+					case jbvArray:
+						appendArray(*state, h);
+						break;
+					case jbvHash:
+						appendValue(*state, h);
+						break;
+					default:
+						elog(PANIC, "wrong parent type: %d", (*state)->v.type);
+				}
+			}
+			break;
+		default:
+			elog(PANIC, "wrong type: %08x", r);
+	}
+
+	return h;
+}
+
+/*
+ * JsonbStringIsNumber
+ * 		fast and cheap check of string
+ */
+bool
+JsonbStringIsNumber(char *string, int len) {
+    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')
+                    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;
+}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 90fa447..7f4bd1b 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -27,6 +27,7 @@
 #include "utils/builtins.h"
 #include "utils/hsearch.h"
 #include "utils/json.h"
+#include "utils/jsonb.h"
 #include "utils/jsonapi.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -63,6 +64,7 @@ static inline Datum each_worker(PG_FUNCTION_ARGS, bool as_text);
 /* semantic action functions for json_each */
 static void each_object_field_start(void *state, char *fname, bool isnull);
 static void each_object_field_end(void *state, char *fname, bool isnull);
+static void each_object_field_end_jsonb(void *state, char *fname, bool isnull);
 static void each_array_start(void *state);
 static void each_scalar(void *state, char *token, JsonTokenType tokentype);
 
@@ -70,6 +72,7 @@ static void each_scalar(void *state, char *token, JsonTokenType tokentype);
 static void elements_object_start(void *state);
 static void elements_array_element_start(void *state, bool isnull);
 static void elements_array_element_end(void *state, bool isnull);
+static void elements_array_element_end_jsonb(void *state, bool isnull);
 static void elements_scalar(void *state, char *token, JsonTokenType tokentype);
 
 /* turn a json object into a hash table */
@@ -218,12 +221,86 @@ typedef struct PopulateRecordsetState
  *
  * This SRF operates in value-per-call mode. It processes the
  * object during the first call, and the keys are simply stashed
- * in an array, whise size is expanded as necessary. This is probably
+ * in an array, whose size is expanded as necessary. This is probably
  * safe enough for a list of keys of a single object, since they are
  * limited in size to NAMEDATALEN and the number of keys is unlikely to
  * be so huge that it has major memory implications.
  */
 
+Datum
+jsonb_object_keys(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	OkeysState *state;
+	int			i;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext    oldcontext;
+		Jsonb           *jb = PG_GETARG_JSONB(0);
+		bool             skipNested = false;
+		JsonbIterator   *it;
+		JsonbValue	     v;
+		int              r = 0;
+
+	 
+		if (JB_ROOT_IS_SCALAR(jb))
+			elog(ERROR,"Cannot call jsonb_object_keys on a scalar");
+		else if (JB_ROOT_IS_ARRAY(jb))
+			elog(ERROR,"Cannot call jsonb_object_keys on an array");
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		state = palloc(sizeof(OkeysState));
+
+		state->result_size = JB_ROOT_COUNT(jb);
+		state->result_count = 0;
+		state->sent_count = 0;
+		state->result = palloc(state->result_size * sizeof(char *));
+
+		it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+		while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+		{
+			skipNested = true;
+			
+			if (r == WJB_KEY)
+			{
+				char * cstr;
+
+				cstr = palloc(v.string.len+1 * sizeof(char));
+				memcpy(cstr,v.string.val, v.string.len);
+				cstr[v.string.len] = '\0';
+				state->result[state->result_count++] = cstr;
+			}
+		}
+
+
+		MemoryContextSwitchTo(oldcontext);
+		funcctx->user_fctx = (void *) state;
+
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	state = (OkeysState *) funcctx->user_fctx;
+
+	if (state->sent_count < state->result_count)
+	{
+		char	   *nxt = state->result[state->sent_count++];
+
+		SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
+	}
+
+	/* cleanup to reduce or eliminate memory leaks */
+	for (i = 0; i < state->result_count; i++)
+		pfree(state->result[i]);
+	pfree(state->result);
+	pfree(state);
+
+	SRF_RETURN_DONE(funcctx);
+}
+
 
 Datum
 json_object_keys(PG_FUNCTION_ARGS)
@@ -234,12 +311,26 @@ json_object_keys(PG_FUNCTION_ARGS)
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		text	   *json = PG_GETARG_TEXT_P(0);
-		JsonLexContext *lex = makeJsonLexContext(json, true);
+		text	   *json; //  = PG_GETARG_TEXT_P(0);
+		JsonLexContext *lex; //  = makeJsonLexContext(json, true);
 		JsonSemAction *sem;
 
 		MemoryContext oldcontext;
 
+		if (get_fn_expr_argtype(fcinfo->flinfo, 0) == JSONOID)
+		{
+			/* just get the text */
+			json = PG_GETARG_TEXT_P(0);
+		}
+		else
+		{
+			Jsonb      *jb = PG_GETARG_JSONB(0);
+			
+			json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+		}
+
+		lex = makeJsonLexContext(json, true);
+
 		funcctx = SRF_FIRSTCALL_INIT();
 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
@@ -359,6 +450,26 @@ json_object_field(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	text       *jsontext;
+	text	   *result;
+	text	   *fname = PG_GETARG_TEXT_P(1);
+	char	   *fnamestr = text_to_cstring(fname);
+
+	jsontext = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	
+	result = get_worker(jsontext, fnamestr, -1, NULL, NULL, -1, false);
+
+	if (result != NULL)
+		PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	else
+		PG_RETURN_NULL();
+
+}
+
+Datum
 json_object_field_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -375,6 +486,26 @@ json_object_field_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field_text(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	text       *jsontext;
+	text	   *result;
+	text	   *fname = PG_GETARG_TEXT_P(1);
+	char	   *fnamestr = text_to_cstring(fname);
+
+	jsontext = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+
+	result = get_worker(jsontext, fnamestr, -1, NULL, NULL, -1, true);
+	
+	if (result != NULL)
+		PG_RETURN_TEXT_P(result);
+	else
+		PG_RETURN_NULL();
+
+}
+
+Datum
 json_array_element(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -390,6 +521,24 @@ json_array_element(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	text       *jsontext;
+	text	   *result;
+	int			element = PG_GETARG_INT32(1);
+
+	jsontext = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	
+	result = get_worker(jsontext, NULL, element, NULL, NULL, -1, false);
+
+	if (result != NULL)
+		PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	else
+		PG_RETURN_NULL();
+}
+
+Datum
 json_array_element_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -405,24 +554,55 @@ json_array_element_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element_text(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	text       *jsontext;
+	text	   *result;
+	int			element = PG_GETARG_INT32(1);
+
+	jsontext = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	
+	result = get_worker(jsontext, NULL, element, NULL, NULL, -1, true);
+
+	if (result != NULL)
+		PG_RETURN_TEXT_P(result);
+	else
+		PG_RETURN_NULL();
+}
+
+Datum
 json_extract_path(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, false);
 }
 
 Datum
+jsonb_extract_path(PG_FUNCTION_ARGS)
+{
+	return get_path_all(fcinfo, false);
+}
+
+Datum
 json_extract_path_text(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, true);
 }
 
+Datum
+jsonb_extract_path_text(PG_FUNCTION_ARGS)
+{
+	return get_path_all(fcinfo, true);
+}
+
 /*
  * common routine for extract_path functions
  */
 static inline Datum
 get_path_all(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
 	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
 	text	   *result;
 	Datum	   *pathtext;
@@ -434,6 +614,19 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	long		ind;
 	char	   *endptr;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
 	if (array_contains_nulls(path))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -472,9 +665,17 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	result = get_worker(json, NULL, -1, tpath, ipath, npath, as_text);
 
 	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
+	{
+		if (val_type == JSONOID || as_text)
+			PG_RETURN_TEXT_P(result);
+		else
+			PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	}
 	else
+	{
+		/* null is null regardless */
 		PG_RETURN_NULL();
+	}
 }
 
 /*
@@ -814,12 +1015,14 @@ get_scalar(void *state, char *token, JsonTokenType tokentype)
 Datum
 json_array_length(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
 	AlenState  *state;
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(AlenState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -839,6 +1042,24 @@ json_array_length(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(state->count);
 }
 
+Datum
+jsonb_array_length(PG_FUNCTION_ARGS)
+{
+	Jsonb *jb = PG_GETARG_JSONB(0);
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a scalar")));
+	else if (! JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a non-array")));
+
+	PG_RETURN_INT32(JB_ROOT_COUNT(jb));
+	
+}
+
 /*
  * These next two check ensure that the json is an array (since it can't be
  * a scalar or an object).
@@ -895,22 +1116,49 @@ json_each(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_each(PG_FUNCTION_ARGS)
+{
+	return each_worker(fcinfo, false);
+}
+
+Datum
 json_each_text(PG_FUNCTION_ARGS)
 {
 	return each_worker(fcinfo, true);
 }
 
+Datum
+jsonb_each_text(PG_FUNCTION_ARGS)
+{
+	return each_worker(fcinfo, true);
+}
+
 static inline Datum
 each_worker(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	JsonLexContext *lex = makeJsonLexContext(json, true);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	TupleDesc	tupdesc;
 	EachState  *state;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
+	lex = makeJsonLexContext(json, true);
 	state = palloc0(sizeof(EachState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -944,7 +1192,10 @@ each_worker(PG_FUNCTION_ARGS, bool as_text)
 	sem->array_start = each_array_start;
 	sem->scalar = each_scalar;
 	sem->object_field_start = each_object_field_start;
-	sem->object_field_end = each_object_field_end;
+	if (val_type == JSONOID)
+		sem->object_field_end = each_object_field_end;
+	else
+		sem->object_field_end = each_object_field_end_jsonb;
 
 	state->normalize_results = as_text;
 	state->next_scalar = false;
@@ -1033,6 +1284,61 @@ each_object_field_end(void *state, char *fname, bool isnull)
 }
 
 static void
+each_object_field_end_jsonb(void *state, char *fname, bool isnull)
+{
+	EachState  *_state = (EachState *) state;
+	MemoryContext old_cxt;
+	int			len;
+	Jsonb	   *val;
+	HeapTuple	tuple;
+	Datum		values[2];
+	bool		nulls[2] = {false, false};
+
+	/* skip over nested objects */
+	if (_state->lex->lex_level != 1)
+		return;
+
+	/* use the tmp context so we can clean up after each tuple is done */
+	old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
+
+	values[0] = CStringGetTextDatum(fname);
+
+	if (isnull && _state->normalize_results)
+	{
+		nulls[1] = true;
+		values[1] = (Datum) NULL;
+	}
+	else if (_state->next_scalar)
+	{
+		values[1] = CStringGetTextDatum(_state->normalized_scalar);
+		_state->next_scalar = false;
+	}
+	else if (_state->normalize_results)
+	{
+		len = _state->lex->prev_token_terminator - _state->result_start;
+		values[1] = PointerGetDatum(cstring_to_text_with_len(_state->result_start, len));
+	}
+	else
+	{
+		char *cstr;
+		len = _state->lex->prev_token_terminator - _state->result_start;
+		cstr = palloc(len+1 * sizeof(char));
+		memcpy(cstr,_state->result_start,len);
+		cstr[len] = '\0';
+		val = DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(cstr)));
+		values[1] = PointerGetDatum(val);
+	}
+
+	tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
+
+	tuplestore_puttuple(_state->tuple_store, tuple);
+
+	/* clean up and switch back */
+	MemoryContextSwitchTo(old_cxt);
+	MemoryContextReset(_state->tmp_cxt);
+}
+
+static void
 each_array_start(void *state)
 {
 	EachState  *_state = (EachState *) state;
@@ -1067,19 +1373,41 @@ each_scalar(void *state, char *token, JsonTokenType tokentype)
  *
  * a lot of this processing is similar to the json_each* functions
  */
+
+Datum
+jsonb_array_elements(PG_FUNCTION_ARGS)
+{
+	return json_array_elements(fcinfo);
+}
+
 Datum
 json_array_elements(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
 
-	/* elements doesn't need any escaped strings, so use false here */
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	TupleDesc	tupdesc;
 	ElementsState *state;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
+	/* elements doesn't need any escaped strings, so use false here */
+	lex  = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(ElementsState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -1114,7 +1442,10 @@ json_array_elements(PG_FUNCTION_ARGS)
 	sem->object_start = elements_object_start;
 	sem->scalar = elements_scalar;
 	sem->array_element_start = elements_array_element_start;
-	sem->array_element_end = elements_array_element_end;
+	if (val_type == JSONOID)
+		sem->array_element_end = elements_array_element_end;
+	else
+		sem->array_element_end = elements_array_element_end_jsonb;
 
 	state->lex = lex;
 	state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
@@ -1174,6 +1505,41 @@ elements_array_element_end(void *state, bool isnull)
 }
 
 static void
+elements_array_element_end_jsonb(void *state, bool isnull)
+{
+	ElementsState *_state = (ElementsState *) state;
+	MemoryContext old_cxt;
+	int			len;
+	Jsonb      *jbval;
+	HeapTuple	tuple;
+	Datum		values[1];
+	static bool nulls[1] = {false};
+	char *cstr;
+
+	/* skip over nested objects */
+	if (_state->lex->lex_level != 1)
+		return;
+
+	/* use the tmp context so we can clean up after each tuple is done */
+	old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
+
+	len = _state->lex->prev_token_terminator - _state->result_start;
+	cstr = palloc(len+1 * sizeof(char));
+	memcpy(cstr,_state->result_start,len);
+	cstr[len] = '\0';
+	jbval = DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(cstr)));
+	values[0] = PointerGetDatum(jbval);
+
+	tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
+
+	tuplestore_puttuple(_state->tuple_store, tuple);
+
+	/* clean up and switch back */
+	MemoryContextSwitchTo(old_cxt);
+	MemoryContextReset(_state->tmp_cxt);
+}
+
+static void
 elements_object_start(void *state)
 {
 	ElementsState *_state = (ElementsState *) state;
@@ -1214,9 +1580,16 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
  * field in the record is then looked up by name.
  */
 Datum
+jsonb_populate_record(PG_FUNCTION_ARGS)
+{
+	return json_populate_record(fcinfo);
+}
+
+Datum
 json_populate_record(PG_FUNCTION_ARGS)
 {
 	Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	Oid         jtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
 	text	   *json;
 	bool		use_json_as_text;
 	HTAB	   *json_hash;
@@ -1234,6 +1607,8 @@ json_populate_record(PG_FUNCTION_ARGS)
 	char		fname[NAMEDATALEN];
 	JsonHashEntry *hashentry;
 
+	Assert(jtype == JSONOID || jtype == JSONBOID);
+
 	use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
 
 	if (!type_is_rowtype(argtype))
@@ -1268,7 +1643,17 @@ json_populate_record(PG_FUNCTION_ARGS)
 		tupTypmod = HeapTupleHeaderGetTypMod(rec);
 	}
 
-	json = PG_GETARG_TEXT_P(1);
+	if (jtype == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(1);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(1);
+		
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
 
 	json_hash = get_json_object_as_hash(json, "json_populate_record", use_json_as_text);
 
@@ -1559,9 +1944,17 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
  * per object in the array.
  */
 Datum
+jsonb_populate_recordset(PG_FUNCTION_ARGS)
+{
+	return json_populate_recordset(fcinfo);
+}
+
+
+Datum
 json_populate_recordset(PG_FUNCTION_ARGS)
 {
 	Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	Oid         jtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
 	text	   *json;
 	bool		use_json_as_text;
 	ReturnSetInfo *rsi;
@@ -1621,7 +2014,17 @@ json_populate_recordset(PG_FUNCTION_ARGS)
 	if (PG_ARGISNULL(1))
 		PG_RETURN_NULL();
 
-	json = PG_GETARG_TEXT_P(1);
+	if (jtype == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(1);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(1);
+		
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
 
 	if (PG_ARGISNULL(0))
 		rec = NULL;
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index ae71bd6..eb37600 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -169,6 +169,8 @@ BuildEventTriggerCache(void)
 			event = EVT_DDLCommandEnd;
 		else if (strcmp(evtevent, "sql_drop") == 0)
 			event = EVT_SQLDrop;
+		else if (strcmp(evtevent, "transaction_commit") == 0)
+			event = EVT_Commit;
 		else
 			continue;
 
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 6aa4890..143a451 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1753,6 +1753,18 @@ DATA(insert OID = 3966 (  "#>"	   PGNSP PGUID b f f 114 1009 114 0 0 json_extrac
 DESCR("get value from json with path elements");
 DATA(insert OID = 3967 (  "#>>"    PGNSP PGUID b f f 114 1009 25 0 0 json_extract_path_text_op - - ));
 DESCR("get value from json as text with path elements");
+DATA(insert OID = 3211 (  "->"	   PGNSP PGUID b f f 3802 25 3802 0 0 jsonb_object_field - - ));
+DESCR("get jsonb object field");
+DATA(insert OID = 3204 (  "->>"    PGNSP PGUID b f f 3802 25 25 0 0 jsonb_object_field_text - - ));
+DESCR("get jsonb object field as text");
+DATA(insert OID = 3212 (  "->"	   PGNSP PGUID b f f 3802 23 3802 0 0 jsonb_array_element - - ));
+DESCR("get jsonb array element");
+DATA(insert OID = 3205 (  "->>"    PGNSP PGUID b f f 3802 23 25 0 0 jsonb_array_element_text - - ));
+DESCR("get jsonb array element as text");
+DATA(insert OID = 3213 (  "#>"	   PGNSP PGUID b f f 3802 1009 3802 0 0 jsonb_extract_path_op - - ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3206 (  "#>>"    PGNSP PGUID b f f 3802 1009 25 0 0 jsonb_extract_path_text_op - - ));
+DESCR("get value from jsonb as text with path elements");
 
 
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b5b1ff7..6959ab7 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4463,6 +4463,50 @@ DESCR("I/O");
 DATA(insert OID = 3774 (  regdictionarysend PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3769" _null_ _null_ _null_ _null_ regdictionarysend _null_ _null_ _null_ ));
 DESCR("I/O");
 
+/* jsonb */
+DATA(insert OID =  3806 (  jsonb_in			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2275" _null_ _null_ _null_ _null_ jsonb_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3805 (  jsonb_recv		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2281" _null_ _null_ _null_ _null_ jsonb_recv _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3804 (  jsonb_out		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3802" _null_ _null_ _null_ _null_ jsonb_out _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3803 (  jsonb_send		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3802" _null_ _null_ _null_ _null_	jsonb_send _null_ _null_ _null_ ));
+DESCR("I/O");
+
+DATA(insert OID = 3969 (  jsonb_object_field			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field _null_ _null_ _null_ ));
+DESCR("get jsonb object field");
+DATA(insert OID = 3179 (  jsonb_object_field_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field_text _null_ _null_ _null_ ));
+DESCR("get jsonb object field as text");
+DATA(insert OID = 3180 (  jsonb_array_element		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element _null_ _null_ _null_ ));
+DESCR("get jsonb array element");
+DATA(insert OID = 3195 (  jsonb_array_element_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element_text _null_ _null_ _null_ ));
+DESCR("get jsonb array element as text");
+DATA(insert OID = 3196 (  jsonb_extract_path			PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 3802 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3199 (  jsonb_extract_path_op		PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3200 (  jsonb_extract_path_text	PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 25 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DESCR("get value from jsonb as text with path elements");
+DATA(insert OID = 3197 (  jsonb_extract_path_text_op PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 25 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DESCR("get value from jsonb as text with path elements");
+DATA(insert OID = 3198 (  jsonb_array_elements		PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 3802 "3802" "{3802,3802}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3207 (  jsonb_array_length			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "3802" _null_ _null_ _null_ _null_ jsonb_array_length _null_ _null_ _null_ ));
+DESCR("length of jsonb array");
+DATA(insert OID = 3201 (  jsonb_object_keys			PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_object_keys _null_ _null_ _null_ ));
+DESCR("get jsonb object keys");
+DATA(insert OID = 3208 (  jsonb_each				   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,3802}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3202 (  jsonb_each_text		   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,25}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each_text _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3209 (  jsonb_populate_record	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_record _null_ _null_ _null_ ));
+DESCR("get record fields from a jsonb object");
+DATA(insert OID = 3203 (  jsonb_populate_recordset  PGNSP PGUID 12 1 100 0 0 f f f f f t s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_recordset _null_ _null_ _null_ ));
+DESCR("get set of records with fields from a jsonb array of objects");
+DATA(insert OID = 3210 (  jsonb_typeof              PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_typeof _null_ _null_ _null_ ));
+DESCR("get the type of a jsonb value");
+
+
 /* txid */
 DATA(insert OID = 2939 (  txid_snapshot_in			PGNSP PGUID 12 1  0 0 0 f f f f t f i 1 0 2970 "2275" _null_ _null_ _null_ _null_ txid_snapshot_in _null_ _null_ _null_ ));
 DESCR("I/O");
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 3fc20c6..7fb8999 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -600,6 +600,12 @@ DATA(insert OID = 3645 ( _tsquery		PGNSP PGUID -1 f b A f t \054 0 3615 0 array_
 DATA(insert OID = 3735 ( _regconfig		PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 
+/* jsonb */
+DATA(insert OID = 3802 ( jsonb			PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DESCR("Binary JSON");
+#define JSONBOID 3802
+DATA(insert OID = 3807 ( _jsonb			PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 DATA(insert OID = 2970 ( txid_snapshot	PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 DESCR("txid snapshot");
 DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 0233f4c..d399e83 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -52,4 +52,6 @@ extern void EventTriggerEndCompleteQuery(void);
 extern bool trackDroppedObjectsNeeded(void);
 extern void EventTriggerSQLDropAddObject(ObjectAddress *object);
 
+extern void PreCommitTriggersFire(void);
+
 #endif   /* EVENT_TRIGGER_H */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 9982e59..3610fc8 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -293,6 +293,15 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
 		PG_RETURN_DATUM(_result); \
 	} while (0)
 
+#define SRF_RETURN_NEXT_NULL(_funcctx) \
+	do { \
+		ReturnSetInfo	   *rsi; \
+		(_funcctx)->call_cntr++; \
+		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+		rsi->isDone = ExprMultipleResult; \
+		PG_RETURN_NULL(); \
+	} while (0)
+
 #define  SRF_RETURN_DONE(_funcctx) \
 	do { \
 		ReturnSetInfo	   *rsi; \
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index c4c284f..0f9ae89 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -20,7 +20,8 @@ typedef enum
 {
 	EVT_DDLCommandStart,
 	EVT_DDLCommandEnd,
-	EVT_SQLDrop
+	EVT_SQLDrop,
+	EVT_Commit
 } EventTriggerEvent;
 
 typedef struct
diff --git a/src/include/utils/json.h b/src/include/utils/json.h
index 25bfafb..c2fc7ee 100644
--- a/src/include/utils/json.h
+++ b/src/include/utils/json.h
@@ -50,4 +50,18 @@ extern Datum json_array_elements(PG_FUNCTION_ARGS);
 extern Datum json_populate_record(PG_FUNCTION_ARGS);
 extern Datum json_populate_recordset(PG_FUNCTION_ARGS);
 
+extern Datum jsonb_object_field(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_field_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_keys(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_length(PG_FUNCTION_ARGS);
+extern Datum jsonb_each(PG_FUNCTION_ARGS);
+extern Datum jsonb_each_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_elements(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_record(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_recordset(PG_FUNCTION_ARGS);
+
 #endif   /* JSON_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
new file mode 100644
index 0000000..0a417c5
--- /dev/null
+++ b/src/include/utils/jsonb.h
@@ -0,0 +1,231 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.h
+ *    Declarations for JSONB data type support.
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/include/utils/jsonb.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef __JSONB_H__
+#define __JSONB_H__
+
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "utils/array.h"
+#include "utils/numeric.h"
+
+/*
+ * JEntry: there is one of these for each key _and_ value in an jsonb
+ *
+ * the position offset points to the _end_ so that we can get the length
+ * by subtraction from the previous entry.	the ISFIRST flag lets us tell
+ * whether there is a previous entry.
+ */
+typedef struct
+{
+	uint32		entry;
+} JEntry;
+
+#define JENTRY_ISFIRST		0x80000000
+#define JENTRY_ISSTRING 	(0x00000000) /* keep binary compatibility */
+#define JENTRY_ISNUMERIC	(0x10000000)
+#define JENTRY_ISNEST		(0x20000000)
+#define JENTRY_ISNULL		(0x40000000) /* keep binary compatibility */
+#define JENTRY_ISBOOL		(0x10000000 | 0x20000000)
+#define JENTRY_ISFALSE		JENTRY_ISBOOL
+#define JENTRY_ISTRUE		(0x10000000 | 0x20000000 | 0x40000000)
+
+/* JENTRY_ISOBJECT, JENTRY_ISARRAY and JENTRY_ISCALAR is only used in send/recv */
+#define JENTRY_ISOBJECT		(0x20000000)
+#define JENTRY_ISARRAY		(0x20000000 | 0x40000000)
+#define JENTRY_ISCALAR		(0x10000000 | 0x40000000)
+
+#define JENTRY_POSMASK 	0x0FFFFFFF
+#define JENTRY_TYPEMASK	(~(JENTRY_POSMASK | JENTRY_ISFIRST))
+
+/* note possible multiple evaluations, also access to prior array element */
+#define JBE_ISFIRST(he_) 		(((he_).entry & JENTRY_ISFIRST) != 0)
+#define JBE_ISSTRING(he_)		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
+#define JBE_ISNUMERIC(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
+#define JBE_ISNEST(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNEST)
+#define JBE_ISNULL(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNULL)
+#define JBE_ISBOOL(he_) 		(((he_).entry & JENTRY_TYPEMASK & JENTRY_ISBOOL) == JENTRY_ISBOOL)
+#define JBE_ISBOOL_TRUE(he_) 	(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISTRUE)
+#define JBE_ISBOOL_FALSE(he_) 	(JBE_ISBOOL(he_) && !JBE_ISBOOL_TRUE(he_))
+
+#define JBE_ENDPOS(he_) ((he_).entry & JENTRY_POSMASK)
+#define JBE_OFF(he_) (JBE_ISFIRST(he_) ? 0 : JBE_ENDPOS((&(he_))[-1]))
+#define JBE_LEN(he_) (JBE_ISFIRST(he_)	\
+					  ? JBE_ENDPOS(he_) \
+					  : JBE_ENDPOS(he_) - JBE_ENDPOS((&(he_))[-1]))
+
+/*
+ * determined by the size of "endpos" (ie JENTRY_POSMASK)
+ */
+#define JSONB_MAX_STRING_LEN 		JENTRY_POSMASK
+
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	/* header of hash or array jsonb type */
+	/* array of JEntry follows */
+} Jsonb;
+
+/*
+ * it's not possible to get more than 2^28 items into an jsonb.
+ */
+#define JB_FLAG_UNUSED 			0x80000000
+#define JB_FLAG_ARRAY			0x40000000
+#define JB_FLAG_OBJECT			0x20000000
+#define JB_FLAG_SCALAR			0x10000000
+
+#define JB_COUNT_MASK			0x0FFFFFFF
+
+#define JB_ISEMPTY(jbp_)		(VARSIZE(jbp_) <= VARHDRSZ)
+#define JB_ROOT_COUNT(jbp_) 	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_COUNT_MASK))
+#define JB_ROOT_IS_OBJECT(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_OBJECT))
+#define JB_ROOT_IS_ARRAY(jbp_) 	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_ARRAY))
+#define JB_ROOT_IS_SCALAR(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_SCALAR))
+
+#define DatumGetJsonb(d) 	((Jsonb*) PG_DETOAST_DATUM(d))
+#define JsonbGetDatum(p)	PointerGetDatum(p)
+
+#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x)
+
+typedef struct JsonbPair JsonbPair;
+typedef struct JsonbValue JsonbValue;
+
+struct JsonbValue {
+	enum {
+		jbvNull,
+		jbvString,
+		jbvNumeric,
+		jbvBool,
+		jbvArray,
+		jbvHash,
+		jbvBinary  /* binary form of jbvArray/jbvHash */
+	} type;
+
+	uint32		size; /* estimation size of node (including subnodes) */
+
+	union {
+		Numeric			numeric;
+		bool			boolean;
+		struct {
+			uint32		len;
+			char 		*val; /* could be not null-terminated */
+		} string;
+
+		struct {
+			int			nelems;
+			JsonbValue	*elems;
+			bool		scalar; /* scalar actually shares representation with array */
+		} array;
+
+		struct {
+			int			npairs;
+			JsonbPair 	*pairs;
+		} hash;
+
+		struct {
+			uint32		len;
+			char		*data;
+		} binary;
+	};
+
+}; 
+
+struct JsonbPair {
+	JsonbValue	key;
+	JsonbValue	value;
+	uint32		order; /* to keep order of pairs with equal key */ 
+}; 
+
+/*
+ * jsonb support functios
+ */
+
+#define WJB_KEY         	(0x001)
+#define WJB_VALUE       	(0x002)
+#define WJB_ELEM       		(0x004)
+#define WJB_BEGIN_ARRAY 	(0x008)
+#define WJB_END_ARRAY   	(0x010)
+#define WJB_BEGIN_OBJECT    (0x020)
+#define WJB_END_OBJECT      (0x040)
+
+typedef void (*walk_jsonb_cb)(void* /*arg*/, JsonbValue* /* value */, 
+											uint32 /* flags */, uint32 /* level */);
+extern void walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg);
+
+extern int compareJsonbStringValue(const void *a, const void *b, void *arg);
+extern int compareJsonbPair(const void *a, const void *b, void *arg);
+
+extern int compareJsonbBinaryValue(char *a, char *b);
+extern int compareJsonbValue(JsonbValue *a, JsonbValue *b);
+
+extern JsonbValue* findUncompressedJsonbValueByValue(char *buffer, uint32 flags, 
+												uint32 *lowbound, JsonbValue* key);
+extern JsonbValue* findUncompressedJsonbValue(char *buffer, uint32 flags, 
+												uint32 *lowbound, char *key, uint32 keylen);
+
+extern JsonbValue* getJsonbValue(char *buffer, uint32 flags, int32 i);
+
+extern bool JsonbStringIsNumber(char *string, int len);
+
+typedef struct ToJsonbState
+{
+	JsonbValue             v;
+	uint32                  size;
+	struct ToJsonbState    *next;
+} ToJsonbState;
+
+extern JsonbValue* pushJsonbValue(ToJsonbState **state, int r /* WJB_* */, JsonbValue *v);
+
+extern void uniqueJsonbValue(JsonbValue *v);
+
+extern uint32 compressJsonb(JsonbValue *v, char *buffer);
+
+typedef struct JsonbIterator
+{
+	uint32					type;
+	uint32					nelems;
+	JEntry					*array;
+	bool					isScalar;
+	char					*data;
+	char					*buffer; /* unparsed buffer */
+
+	int						i;
+
+	/*
+	 * enum members should be freely OR'ed with JB_FLAG_ARRAY/JB_FLAG_JSONB 
+	 * with possiblity of decoding. See optimization in JsonbIteratorGet()
+	 */
+	enum {
+		jbi_start 	= 0x00,
+		jbi_key		= 0x01,
+		jbi_value	= 0x02,
+		jbi_elem	= 0x04
+	} state;
+
+	struct JsonbIterator	*next;
+} JsonbIterator;
+
+extern 	JsonbIterator*	JsonbIteratorInit(char *buffer);
+extern	int /* WJB_* */	JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested);
+
+extern Datum jsonb_in(PG_FUNCTION_ARGS);
+extern Datum jsonb_out(PG_FUNCTION_ARGS);
+extern Datum jsonb_recv(PG_FUNCTION_ARGS);
+extern Datum jsonb_send(PG_FUNCTION_ARGS);
+
+extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
+
+extern char *JsonbToCString(StringInfo out, char *in, int estimated_len);
+
+#endif   /* __JSONB_H__ */
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
new file mode 100644
index 0000000..d127bc9
--- /dev/null
+++ b/src/test/regress/expected/jsonb.out
@@ -0,0 +1,838 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ octet_length 
+--------------
+            5
+(1 row)
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot extract element from a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot extract field from a non-object
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot extract element from a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  cannot extract array element from a non-array
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  Cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ correct_in_utf8 
+-----------------
+              10
+(1 row)
+
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+   correct_in_utf8    
+----------------------
+ the Copyright © sign
+(1 row)
+
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
new file mode 100644
index 0000000..e3cb5d6
--- /dev/null
+++ b/src/test/regress/expected/jsonb_1.out
@@ -0,0 +1,838 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT octet_length('"\uaBcD"'::jsonb::text);
+                            ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: ...
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot extract element from a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot extract field from a non-object
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot extract element from a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  cannot extract array element from a non-array
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  Cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc3...
+                                   ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a'...
+                     ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 5758b07..51238be 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -98,8 +98,7 @@ test: event_trigger
 # ----------
 # Another group of parallel tests
 # ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json indirect_toast
-
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast
 # ----------
 # Another group of parallel tests
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 78348f5..e414ec1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -121,6 +121,7 @@ test: xmlmap
 test: functional_deps
 test: advisory_lock
 test: json
+test: jsonb
 test: indirect_toast
 test: plancache
 test: limit
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
new file mode 100644
index 0000000..0d0d14c
--- /dev/null
+++ b/src/test/regress/sql/jsonb.sql
@@ -0,0 +1,261 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+SELECT '"abc"'::jsonb;			-- OK
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+SELECT '0'::jsonb;				-- OK
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+SELECT '0.1'::jsonb;				-- OK
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+SELECT '1e100'::jsonb;			-- OK
+SELECT '1.3e100'::jsonb;			-- OK
+SELECT '1f2'::jsonb;				-- ERROR
+SELECT '0.x1'::jsonb;			-- ERROR
+SELECT '1.3ex100'::jsonb;		-- ERROR
+
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+SELECT '[1,2]'::jsonb;			-- OK
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+SELECT '{"abc":1}'::jsonb;		-- OK
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+SELECT 'false'::jsonb;			-- OK
+SELECT 'null'::jsonb;			-- OK
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+SELECT ''::jsonb;				-- ERROR, no value
+SELECT '    '::jsonb;			-- ERROR, no value
+
+-- jsonb extraction functions
+
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+
+-- nulls
+
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+
+
+-- array length
+
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+
+SELECT jsonb_array_length('[]');
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+
+SELECT jsonb_array_length('4');
+
+-- each
+
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+-- extract_path, extract_path_as_text
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+
+-- extract_path nulls
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+
+-- extract_path operators
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+
+-- array_elements
+
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+
+-- populate_recordset
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+
+-- using the default use_json_as_text argument
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+
+
+-- handling of unicode surrogate pairs
+
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+
+--handling of simple unicode escapes
+
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
#41Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Andrew Dunstan (#40)
Re: nested hstore patch

Andrew Dunstan wrote:

On 01/13/2014 03:25 AM, Erik Rijkers wrote:

There are errors in the example expressions in "Table F-6. hstore Operators".

Attached is a cumulative doc-patch (which includes the changes I sent earlier) which fixes these.

I also attach an test perl program that shows the (small) differences in output between what's in that doc table and what
one actually gets. (I found these too insignificant to change but perhaps you have a different opinion.)

A new version of the patch is attached. It includes all of Erik's
docs fixes and a small fix by Alexander Korotkov for hstore hash
ops.

Interestingly, this also include transaction_commit event triggers.

There are also a few PANIC elogs, probably not what's intended.

(I was just giving this a quick skim to see if there's support to build
JSON objects incrementally from C source, i.e. not have to call
functions using the fmgr interface. Apparently that's not the case, but
if I'm wrong please let me know.)

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Oleg Bartunov
obartunov@gmail.com
In reply to: Andrew Dunstan (#40)
Re: nested hstore patch

Andrew,

did you run perl script ? Actually, I found, that operator table needs
to be fixed.

Oleg

On Mon, Jan 13, 2014 at 7:36 PM, Andrew Dunstan <andrew@dunslane.net> wrote:

On 01/13/2014 03:25 AM, Erik Rijkers wrote:

There are errors in the example expressions in "Table F-6. hstore
Operators".

Attached is a cumulative doc-patch (which includes the changes I sent
earlier) which fixes these.

I also attach an test perl program that shows the (small) differences in
output between what's in that doc table and what
one actually gets. (I found these too insignificant to change but perhaps
you have a different opinion.)

A new version of the patch is attached. It includes all of Erik's docs fixes
and a small fix by Alexander Korotkov for hstore hash ops.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#43Andrew Dunstan
andrew@dunslane.net
In reply to: Alvaro Herrera (#41)
Re: nested hstore patch

On 01/13/2014 11:03 AM, Alvaro Herrera wrote:

Andrew Dunstan wrote:

On 01/13/2014 03:25 AM, Erik Rijkers wrote:

There are errors in the example expressions in "Table F-6. hstore Operators".

Attached is a cumulative doc-patch (which includes the changes I sent earlier) which fixes these.

I also attach an test perl program that shows the (small) differences in output between what's in that doc table and what
one actually gets. (I found these too insignificant to change but perhaps you have a different opinion.)

A new version of the patch is attached. It includes all of Erik's
docs fixes and a small fix by Alexander Korotkov for hstore hash
ops.

Interestingly, this also include transaction_commit event triggers.

Oh, wow, really? git really did something horrible, or I did
inadvertently. This is what comes from using the same directory for
multiple development lines :-(

Will fix

There are also a few PANIC elogs, probably not what's intended.

Oleg, Teodor, please address.

(I was just giving this a quick skim to see if there's support to build
JSON objects incrementally from C source, i.e. not have to call
functions using the fmgr interface. Apparently that's not the case, but
if I'm wrong please let me know.)

Erm, maybe you need the other json patch:
</messages/by-id/52C76B33.1050808@dunslane.net&gt;

If we need to adjust some of that a bit to make it more friendly for
internal use I'm happy to try to do that. Unfortunately, I don't think
that's terribly easy for VARIADIC "any" functions like these.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Andrew Dunstan
andrew@dunslane.net
In reply to: Oleg Bartunov (#42)
Re: nested hstore patch

On 01/13/2014 11:16 AM, Oleg Bartunov wrote:

Andrew,

did you run perl script ? Actually, I found, that operator table needs
to be fixed.

No. My build machine doesn't actually have DBD::Pg installed. Can you
send me a patch if you don't want to push it yourself, or maybe Erik can
send a pacth top adjust the table.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#45Erik Rijkers
er@xs4all.nl
In reply to: Andrew Dunstan (#44)
2 attachment(s)
Re: nested hstore patch

On Mon, January 13, 2014 18:30, Andrew Dunstan wrote:

On 01/13/2014 11:16 AM, Oleg Bartunov wrote:

Andrew,

did you run perl script ? Actually, I found, that operator table needs
to be fixed.

No. My build machine doesn't actually have DBD::Pg installed. Can you
send me a patch if you don't want to push it yourself, or maybe Erik can
send a pacth top adjust the table.

[ nested_hstore_and_jsonb-2.patch ]

( centos 6.5, gcc 4.8.2. )

The patch applies & compiles with warnings (see below).

The opr_sanity test fails during make check: regression.diffs attached.

Also attached are changes to hstore.sgml, to operator + functions table, plus some typos.

Thanks,
Erik Rijkers

make

jsonfuncs.c: In function �each_object_field_end_jsonb�:
jsonfuncs.c:1328:7: warning: assignment from incompatible pointer type [enabled by default]
val = DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(cstr)));
^
jsonfuncs.c: In function �elements_array_element_end_jsonb�:
jsonfuncs.c:1530:8: warning: assignment from incompatible pointer type [enabled by default]
jbval = DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(cstr)));
^

make contrib:

hstore_io.c: In function �array_to_hstore�:
hstore_io.c:1694:30: warning: �result� may be used uninitialized in this function [-Wmaybe-uninitialized]
PG_RETURN_POINTER(hstoreDump(result));

Attachments:

regression.diffsapplication/octet-stream; name=regression.diffsDownload
*** /home/aardvark/pg_stuff/pg_sandbox/pgsql.nested_hstore/src/test/regress/expected/opr_sanity.out	2014-01-13 23:31:40.029522824 +0100
--- /home/aardvark/pg_stuff/pg_sandbox/pgsql.nested_hstore/src/test/regress/results/opr_sanity.out	2014-01-13 23:36:15.603044193 +0100
***************
*** 718,731 ****
    WHERE prodesc IS DISTINCT FROM expecteddesc
      AND oprdesc NOT LIKE 'deprecated%'
  ORDER BY 1;
!  p_oid |    proname    |               prodesc               
! -------+---------------+-------------------------------------
!    378 | array_append  | append element onto end of array
!    379 | array_prepend | prepend element onto front of array
!   1035 | aclinsert     | add/update ACL item
!   1036 | aclremove     | remove ACL item
!   1037 | aclcontains   | contains
! (5 rows)
  
  -- **************** pg_aggregate ****************
  -- Look for illegal values in pg_aggregate fields.
--- 718,737 ----
    WHERE prodesc IS DISTINCT FROM expecteddesc
      AND oprdesc NOT LIKE 'deprecated%'
  ORDER BY 1;
!  p_oid |          proname           |                     prodesc                     
! -------+----------------------------+-------------------------------------------------
!    378 | array_append               | append element onto end of array
!    379 | array_prepend              | prepend element onto front of array
!   1035 | aclinsert                  | add/update ACL item
!   1036 | aclremove                  | remove ACL item
!   1037 | aclcontains                | contains
!   3179 | jsonb_object_field_text    | get jsonb object field as text
!   3180 | jsonb_array_element        | get jsonb array element
!   3195 | jsonb_array_element_text   | get jsonb array element as text
!   3197 | jsonb_extract_path_text_op | get value from jsonb as text with path elements
!   3199 | jsonb_extract_path_op      | get value from jsonb with path elements
!   3969 | jsonb_object_field         | get jsonb object field
! (11 rows)
  
  -- **************** pg_aggregate ****************
  -- Look for illegal values in pg_aggregate fields.

======================================================================

hstore.sgml.20140114.difftext/x-patch; name=hstore.sgml.20140114.diffDownload
--- doc/src/sgml/hstore.sgml.orig	2014-01-14 00:06:30.070883763 +0100
+++ doc/src/sgml/hstore.sgml	2014-01-14 00:58:53.069334810 +0100
@@ -350,7 +350,7 @@
       <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>
@@ -422,7 +422,7 @@
       <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>
+      <entry><literal>"a"=&gt;1, "c"=&gt;3</literal></entry>
      </row>
 
      <row>
@@ -438,7 +438,7 @@
       <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>
@@ -446,14 +446,14 @@
       <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>
+      <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; [1,2]}}}'::hstore #- '{a,b,c,0}'</literal></entry>
       <entry><literal>"a"=&gt;{"b"=&gt;{"c"=&gt;[2]}}</literal></entry>
      </row>
 
@@ -525,7 +525,7 @@
       <entry><type>hstore</type></entry>
       <entry>construct an <type>hstore</> from a record or row</entry>
       <entry><literal>hstore(ROW(1,2))</literal></entry>
-      <entry><literal>f1=&gt;1,f2=&gt;2</literal></entry>
+      <entry><literal>"f1"=&gt;"1","f2"=&gt;"2"</literal></entry>
      </row>
 
      <row>
@@ -534,7 +534,7 @@
       <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>
@@ -707,7 +707,7 @@
       <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>
@@ -766,15 +766,15 @@
       <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;}</literal></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>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>
 
@@ -783,7 +783,7 @@
       <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 +791,7 @@
       <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,7 +799,7 @@
       <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>
@@ -1007,7 +1007,7 @@
 
   <para>
    But <literal>populate_record()</> supports more complicated records and nested
-   <type>hstore</> values, as well. It makes an effort to convert
+   <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>
@@ -1124,7 +1124,7 @@
   <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
+   introduced in PostgreSQL 9.0. These changes present 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
#46Oleg Bartunov
obartunov@gmail.com
In reply to: Erik Rijkers (#45)
Re: nested hstore patch

Erik,

thanks for docs fixes, we have even more :)

Oleg

On Tue, Jan 14, 2014 at 4:18 AM, Erik Rijkers <er@xs4all.nl> wrote:

On Mon, January 13, 2014 18:30, Andrew Dunstan wrote:

On 01/13/2014 11:16 AM, Oleg Bartunov wrote:

Andrew,

did you run perl script ? Actually, I found, that operator table needs
to be fixed.

No. My build machine doesn't actually have DBD::Pg installed. Can you
send me a patch if you don't want to push it yourself, or maybe Erik can
send a pacth top adjust the table.

[ nested_hstore_and_jsonb-2.patch ]

( centos 6.5, gcc 4.8.2. )

The patch applies & compiles with warnings (see below).

The opr_sanity test fails during make check: regression.diffs attached.

Also attached are changes to hstore.sgml, to operator + functions table, plus some typos.

Thanks,
Erik Rijkers

make

jsonfuncs.c: In function ‘each_object_field_end_jsonb’:
jsonfuncs.c:1328:7: warning: assignment from incompatible pointer type [enabled by default]
val = DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(cstr)));
^
jsonfuncs.c: In function ‘elements_array_element_end_jsonb’:
jsonfuncs.c:1530:8: warning: assignment from incompatible pointer type [enabled by default]
jbval = DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(cstr)));
^

make contrib:

hstore_io.c: In function ‘array_to_hstore’:
hstore_io.c:1694:30: warning: ‘result’ may be used uninitialized in this function [-Wmaybe-uninitialized]
PG_RETURN_POINTER(hstoreDump(result));

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#47Erik Rijkers
er@xs4all.nl
In reply to: Andrew Dunstan (#40)
Re: nested hstore patch - FailedAssertion("!(value->array.nelems == 1)

On Mon, January 13, 2014 16:36, Andrew Dunstan wrote:

A new version of the patch is attached. It includes all of Erik's docs

[ nested_hstore_and_jsonb-2.patch ]

This crashes the server:

testdb=# select 'x' || ('a=>"1"':: hstore) ;
The connection to the server was lost. Attempting reset: Failed.

logging:
TRAP: FailedAssertion("!(value->array.nelems == 1)", File: "jsonb_support.c", Line: 904)
2014-01-15 00:32:01.854 CET 1206 LOG: server process (PID 3918) was terminated by signal 6: Aborted
2014-01-15 00:32:01.854 CET 1206 DETAIL: Failed process was running: select 'x' || ('a=>"1"':: hstore) ;

Btw, I find it strange that:

testdb=# select ('a=>""':: hstore) #%> '{a}' ;
?column?
----------
""
(1 row)

so that:

Time: 0.641 ms
testdb=# select ( ('a=>""':: hstore) #%> '{a}' ) = '' ;
?column?
----------
f
(1 row)

testdb=# select ( ('a=>""':: hstore) #%> '{a}' ) = '""' ;
?column?
----------
t
(1 row)

Maybe there is a rationale, but it seems to me that
('a=>""':: hstore) #%> '{a}'
should deliver the empty string '', and not two double quotes.

Thanks,

Erik Rijkers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#48Oleg Bartunov
obartunov@gmail.com
In reply to: Erik Rijkers (#47)
Re: nested hstore patch - FailedAssertion("!(value->array.nelems == 1)

It doesn't crashed in the last version in our repository.

=# select 'x'::hstore || ('a=>"1"':: hstore) ;
?column?
---------------
"x", "a", "1"
(1 row)

On Wed, Jan 15, 2014 at 3:53 AM, Erik Rijkers <er@xs4all.nl> wrote:

On Mon, January 13, 2014 16:36, Andrew Dunstan wrote:

A new version of the patch is attached. It includes all of Erik's docs

[ nested_hstore_and_jsonb-2.patch ]

This crashes the server:

testdb=# select 'x' || ('a=>"1"':: hstore) ;
The connection to the server was lost. Attempting reset: Failed.

logging:
TRAP: FailedAssertion("!(value->array.nelems == 1)", File: "jsonb_support.c", Line: 904)
2014-01-15 00:32:01.854 CET 1206 LOG: server process (PID 3918) was terminated by signal 6: Aborted
2014-01-15 00:32:01.854 CET 1206 DETAIL: Failed process was running: select 'x' || ('a=>"1"':: hstore) ;

Btw, I find it strange that:

testdb=# select ('a=>""':: hstore) #%> '{a}' ;
?column?
----------
""
(1 row)

so that:

Time: 0.641 ms
testdb=# select ( ('a=>""':: hstore) #%> '{a}' ) = '' ;
?column?
----------
f
(1 row)

testdb=# select ( ('a=>""':: hstore) #%> '{a}' ) = '""' ;
?column?
----------
t
(1 row)

Maybe there is a rationale, but it seems to me that
('a=>""':: hstore) #%> '{a}'
should deliver the empty string '', and not two double quotes.

Thanks,

Erik Rijkers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#49Erik Rijkers
er@xs4all.nl
In reply to: Oleg Bartunov (#48)
Re: nested hstore patch - FailedAssertion("!(value->array.nelems == 1)

On Wed, January 15, 2014 08:01, Oleg Bartunov wrote:

It doesn't crashed in the last version in our repository.

=# select 'x'::hstore || ('a=>"1"':: hstore) ;
?column?
---------------
"x", "a", "1"
(1 row)

OK, shall I use that repository instead of the latest posted patch?
No point in testing old code ( I used nested_hstore_and_jsonb-2.patch ).

Could you send a link to where that repository is?

( btw, your query is not quite the same as the one I used:
select 'x' || ('a=>"1"':: hstore)
but your query also crashes my server here so I suppose
it triggers the same bug )

thanks,

Erik Rijkers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#50Oleg Bartunov
obartunov@gmail.com
In reply to: Erik Rijkers (#49)
Re: nested hstore patch - FailedAssertion("!(value->array.nelems == 1)

https://github.com/feodor/postgres

On Wed, Jan 15, 2014 at 11:10 AM, Erik Rijkers <er@xs4all.nl> wrote:

On Wed, January 15, 2014 08:01, Oleg Bartunov wrote:

It doesn't crashed in the last version in our repository.

=# select 'x'::hstore || ('a=>"1"':: hstore) ;
?column?
---------------
"x", "a", "1"
(1 row)

OK, shall I use that repository instead of the latest posted patch?
No point in testing old code ( I used nested_hstore_and_jsonb-2.patch ).

Could you send a link to where that repository is?

( btw, your query is not quite the same as the one I used:
select 'x' || ('a=>"1"':: hstore)
but your query also crashes my server here so I suppose
it triggers the same bug )

thanks,

Erik Rijkers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#51Erik Rijkers
er@xs4all.nl
In reply to: Oleg Bartunov (#50)
Re: nested hstore patch - FailedAssertion("!(value->array.nelems == 1)

On Wed, January 15, 2014 09:46, Oleg Bartunov wrote:

On Wed, January 15, 2014 08:01, Oleg Bartunov wrote:

It doesn't crashed in the last version in our repository.

=# select 'x'::hstore || ('a=>"1"':: hstore) ;
?column?
---------------
"x", "a", "1"
(1 row)

OK, shall I use that repository instead of the latest posted patch?

I now installed from:
https://github.com/feodor/postgres

and compiled both a 'fast' and a 'debug' server (=with --enable-cassert see [1]pg_config: '--prefix=/home/aardvark/pg_stuff/pg_installations/pgsql.nested_hstore_url' '--bindir=/home/aardvark/pg_stuff/pg_installations/pgsql.nested_hstore_url/bin' '--libdir=/home/aardvark/pg_stuff/pg_installations/pgsql.nested_hstore_url/lib' '--with-pgport=46541' '--enable-depend' '--enable-cassert' '--enable-debug' '--with-openssl' '--with-perl' '--with-libxml' '--with-libxslt' '--with-zlib')

It turns out that the statement does not crash on a server compiled without --enable-cassert.

But a compile with --enable-cassert shows that a bug is still lurking:

testdb=# select 'x'::hstore || ('a=>"1"':: hstore) ;
The connection to the server was lost. Attempting reset: Failed.
!>

TRAP: FailedAssertion("!(value->array.nelems == 1)", File: "hstore_support.c", Line: 896)

Not good.

( please note that the assert is in a different file ('hstore_support.c') from the earlier assert error that I posted )

Thanks,

Erik Rijkers

[1]: pg_config: '--prefix=/home/aardvark/pg_stuff/pg_installations/pgsql.nested_hstore_url' '--bindir=/home/aardvark/pg_stuff/pg_installations/pgsql.nested_hstore_url/bin' '--libdir=/home/aardvark/pg_stuff/pg_installations/pgsql.nested_hstore_url/lib' '--with-pgport=46541' '--enable-depend' '--enable-cassert' '--enable-debug' '--with-openssl' '--with-perl' '--with-libxml' '--with-libxslt' '--with-zlib'
pg_config:
'--prefix=/home/aardvark/pg_stuff/pg_installations/pgsql.nested_hstore_url'
'--bindir=/home/aardvark/pg_stuff/pg_installations/pgsql.nested_hstore_url/bin'
'--libdir=/home/aardvark/pg_stuff/pg_installations/pgsql.nested_hstore_url/lib' '--with-pgport=46541' '--enable-depend'
'--enable-cassert' '--enable-debug' '--with-openssl' '--with-perl' '--with-libxml' '--with-libxslt' '--with-zlib'

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#52Andrew Dunstan
andrew@dunslane.net
In reply to: Erik Rijkers (#51)
1 attachment(s)
Re: nested hstore patch - FailedAssertion("!(value->array.nelems == 1)

On 01/15/2014 05:32 AM, Erik Rijkers wrote:

On Wed, January 15, 2014 09:46, Oleg Bartunov wrote:

On Wed, January 15, 2014 08:01, Oleg Bartunov wrote:

It doesn't crashed in the last version in our repository.

=# select 'x'::hstore || ('a=>"1"':: hstore) ;
?column?
---------------
"x", "a", "1"
(1 row)

OK, shall I use that repository instead of the latest posted patch?

You can always check the jsonb_and_hstore branch on the repo for the
latest and greatest.

But here is the latest patch from that.

cheers

andrew

Attachments:

nested_hstore-3.patchtext/x-patch; name=nested_hstore-3.patchDownload
diff --git a/contrib/hstore/.gitignore b/contrib/hstore/.gitignore
index 5dcb3ff..b84aac6 100644
--- a/contrib/hstore/.gitignore
+++ b/contrib/hstore/.gitignore
@@ -2,3 +2,6 @@
 /log/
 /results/
 /tmp_check/
+/hstore_gram.c
+/hstore_gram.h
+/hstore_scan.c
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 43b7e5f..fb9421f 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -2,13 +2,16 @@
 
 MODULE_big = hstore
 OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
-	crc32.o
+		hstore_gram.o
 
 EXTENSION = hstore
-DATA = hstore--1.2.sql hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
-	hstore--unpackaged--1.0.sql
+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
 
-REGRESS = hstore
+REGRESS = hstore nested types
+
+EXTRA_CLEAN = y.tab.c y.tab.h \
+				hstore_gram.c hstore_scan.c hstore_gram.h
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
@@ -20,3 +23,13 @@ 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
deleted file mode 100644
index c82fc66..0000000
--- a/contrib/hstore/crc32.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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 <srg@quick.com>
- */
-
-#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
deleted file mode 100644
index f5bfd82..0000000
--- a/contrib/hstore/crc32.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * 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/expected/hstore.out b/contrib/hstore/expected/hstore.out
index 2114143..c420c8b 100644
--- a/contrib/hstore/expected/hstore.out
+++ b/contrib/hstore/expected/hstore.out
@@ -199,6 +199,24 @@ select 'aa=>"NuLl"'::hstore;
  "aa"=>"NuLl"
 (1 row)
 
+select 'aa=>nul'::hstore;
+   hstore    
+-------------
+ "aa"=>"nul"
+(1 row)
+
+select 'aa=>NuL'::hstore;
+   hstore    
+-------------
+ "aa"=>"NuL"
+(1 row)
+
+select 'aa=>"NuL"'::hstore;
+   hstore    
+-------------
+ "aa"=>"NuL"
+(1 row)
+
 select e'\\=a=>q=w'::hstore;
    hstore    
 -------------
@@ -432,63 +450,63 @@ select hstore 'a=>NULL, b=>qq' ?& '{}'::text[];
 
 -- delete
 select delete('a=>1 , b=>2, c=>3'::hstore, 'a');
-       delete       
---------------------
- "b"=>"2", "c"=>"3"
+     delete     
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>null , b=>2, c=>3'::hstore, 'a');
-       delete       
---------------------
- "b"=>"2", "c"=>"3"
+     delete     
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'b');
-       delete       
---------------------
- "a"=>"1", "c"=>"3"
+     delete     
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'c');
-       delete       
---------------------
- "a"=>"1", "b"=>"2"
+     delete     
+----------------
+ "a"=>1, "b"=>2
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'd');
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'a'::text;
-      ?column?      
---------------------
- "b"=>"2", "c"=>"3"
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>null , b=>2, c=>3'::hstore - 'a'::text;
-      ?column?      
---------------------
- "b"=>"2", "c"=>"3"
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'b'::text;
-      ?column?      
---------------------
- "a"=>"1", "c"=>"3"
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'c'::text;
-      ?column?      
---------------------
- "a"=>"1", "b"=>"2"
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'd'::text;
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text)
@@ -500,21 +518,21 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text)
 
 -- delete (array)
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','e']);
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','b']);
-       delete       
---------------------
- "a"=>"1", "c"=>"3"
+     delete     
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['a','c']);
-  delete  
-----------
- "b"=>"2"
+ delete 
+--------
+ "b"=>2
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]);
@@ -524,27 +542,27 @@ select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]);
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, '{}'::text[]);
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','e'];
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','b'];
-      ?column?      
---------------------
- "a"=>"1", "c"=>"3"
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c'];
  ?column? 
 ----------
- "b"=>"2"
+ "b"=>2
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']];
@@ -554,9 +572,9 @@ select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']];
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - '{}'::text[];
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c'])
@@ -575,15 +593,15 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - '{}'::text[])
 
 -- delete (hstore)
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>4, b=>2'::hstore);
-       delete        
----------------------
- "c"=>"3", "aa"=>"1"
+     delete      
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>NULL, c=>3'::hstore);
-       delete        
----------------------
- "b"=>"2", "aa"=>"1"
+     delete      
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore);
@@ -593,27 +611,27 @@ select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore);
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'b=>2'::hstore);
-       delete        
----------------------
- "c"=>"3", "aa"=>"1"
+     delete      
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, ''::hstore);
-            delete             
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+         delete          
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>4, b=>2'::hstore;
-      ?column?       
----------------------
- "c"=>"3", "aa"=>"1"
+    ?column?     
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>NULL, c=>3'::hstore;
-      ?column?       
----------------------
- "b"=>"2", "aa"=>"1"
+    ?column?     
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore;
@@ -623,15 +641,15 @@ select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore;
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore;
-      ?column?       
----------------------
- "c"=>"3", "aa"=>"1"
+    ?column?     
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - ''::hstore;
-           ?column?            
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+        ?column?         
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore)
@@ -650,33 +668,33 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ''::hstore)
 
 -- ||
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f';
-                 ?column?                  
--------------------------------------------
- "b"=>"g", "aa"=>"1", "cq"=>"l", "fg"=>"f"
+               ?column?                
+---------------------------------------
+ "b"=>"g", "aa"=>1, "cq"=>"l", "fg"=>f
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'aq=>l';
-                 ?column?                  
--------------------------------------------
- "b"=>"2", "aa"=>"1", "aq"=>"l", "cq"=>"3"
+              ?column?               
+-------------------------------------
+ "b"=>2, "aa"=>1, "aq"=>"l", "cq"=>3
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'aa=>l';
-            ?column?            
---------------------------------
- "b"=>"2", "aa"=>"l", "cq"=>"3"
+          ?column?          
+----------------------------
+ "b"=>2, "aa"=>"l", "cq"=>3
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || '';
-            ?column?            
---------------------------------
- "b"=>"2", "aa"=>"1", "cq"=>"3"
+         ?column?         
+--------------------------
+ "b"=>2, "aa"=>1, "cq"=>3
 (1 row)
 
 select ''::hstore || 'cq=>l, b=>g, fg=>f';
-            ?column?            
---------------------------------
- "b"=>"g", "cq"=>"l", "fg"=>"f"
+           ?column?           
+------------------------------
+ "b"=>"g", "cq"=>"l", "fg"=>f
 (1 row)
 
 select pg_column_size(''::hstore || ''::hstore) = pg_column_size(''::hstore);
@@ -759,21 +777,21 @@ select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']);
-       slice        
---------------------
- "b"=>"2", "c"=>"3"
+     slice      
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['aa','b']);
-        slice        
----------------------
- "b"=>"2", "aa"=>"1"
+      slice      
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b','aa']);
-             slice             
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+          slice          
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select pg_column_size(slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']))
@@ -907,9 +925,9 @@ select pg_column_size(hstore(ARRAY['a','b','asd'], ARRAY['g','h','i']))
 
 -- records
 select hstore(v) from (values (1, 'foo', 1.2, 3::float8)) v(a,b,c,d);
-                   hstore                   
---------------------------------------------
- "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3"
+                hstore                
+--------------------------------------
+ "a"=>1, "b"=>"foo", "c"=>1.2, "d"=>3
 (1 row)
 
 create domain hstestdom1 as integer not null default 0;
@@ -918,9 +936,9 @@ create table testhstore1 (a integer, b text, c numeric, d float8, e hstestdom1);
 insert into testhstore0 values (1, 'foo', 1.2, 3::float8);
 insert into testhstore1 values (1, 'foo', 1.2, 3::float8);
 select hstore(v) from testhstore1 v;
-                        hstore                        
-------------------------------------------------------
- "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3", "e"=>"0"
+                    hstore                    
+----------------------------------------------
+ "a"=>1, "b"=>"foo", "c"=>1.2, "d"=>3, "e"=>0
 (1 row)
 
 select hstore(null::testhstore0);
@@ -936,7 +954,7 @@ select hstore(null::testhstore1);
 (1 row)
 
 select pg_column_size(hstore(v))
-         = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore)
+         = pg_column_size('a=>1, b=>"foo", c=>1.2, d=>3, e=>0'::hstore)
   from testhstore1 v;
  ?column? 
 ----------
@@ -1336,6 +1354,7 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
     42
 (1 row)
 
+RESET enable_seqscan;
 drop index hidx;
 create index hidx on testhstore using gin (h);
 set enable_seqscan=off;
@@ -1375,6 +1394,7 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
     42
 (1 row)
 
+RESET enable_seqscan;
 select count(*) from (select (each(h)).key from testhstore) as wow ;
  count 
 -------
@@ -1437,6 +1457,8 @@ select distinct * from (values (hstore '' || ''),('')) v(h);
 (1 row)
 
 set enable_sort = true;
+RESET enable_hashagg;
+RESET enable_sort;
 -- btree
 drop index hidx;
 create index hidx on testhstore using btree (h);
@@ -1444,7 +1466,7 @@ set enable_seqscan=off;
 select count(*) from testhstore where h #># 'p=>1';
  count 
 -------
-   125
+   884
 (1 row)
 
 select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
@@ -1453,39 +1475,63 @@ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexe
      1
 (1 row)
 
+--gin hash
+drop index hidx;
+create index hidx on testhstore using gin (h gin_hstore_hash_ops);
+set enable_seqscan=off;
+select count(*) from testhstore where h @> 'wait=>NULL';
+ count 
+-------
+     1
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC';
+ count 
+-------
+    15
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+ count 
+-------
+     2
+(1 row)
+
+RESET enable_seqscan;
+drop index hidx;
 -- json
 select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
-                                         hstore_to_json                                          
--------------------------------------------------------------------------------------------------
- {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+                                   hstore_to_json                                   
+------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
-                                              json                                               
--------------------------------------------------------------------------------------------------
- {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+                                        json                                        
+------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
-                                   hstore_to_json_loose                                   
-------------------------------------------------------------------------------------------
- {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}
+                                hstore_to_json_loose                                
+------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 create table test_json_agg (f1 text, f2 hstore);
 insert into test_json_agg values ('rec1','"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'),
        ('rec2','"a key" =>2, b => f, c => "null", d=> -12345, e => 012345.6, f=> -1.234, g=> 0.345e-4');
 select json_agg(q) from test_json_agg q;
-                                                          json_agg                                                          
-----------------------------------------------------------------------------------------------------------------------------
- [{"f1":"rec1","f2":{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}},      +
-  {"f1":"rec2","f2":{"b": "f", "c": "null", "d": "-12345", "e": "012345.6", "f": "-1.234", "g": "0.345e-4", "a key": "2"}}]
+                                                      json_agg                                                      
+--------------------------------------------------------------------------------------------------------------------
+ [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}},           +
+  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": 12345.6, "f": -1.234, "g": 0.0000345, "a key": 2}}]
 (1 row)
 
 select json_agg(q) from (select f1, hstore_to_json_loose(f2) as f2 from test_json_agg) q;
-                                                       json_agg                                                       
-----------------------------------------------------------------------------------------------------------------------
- [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}},       +
-  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.345e-4, "a key": 2}}]
+                                                      json_agg                                                      
+--------------------------------------------------------------------------------------------------------------------
+ [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}},           +
+  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": 12345.6, "f": -1.234, "g": 0.0000345, "a key": 2}}]
 (1 row)
 
diff --git a/contrib/hstore/expected/nested.out b/contrib/hstore/expected/nested.out
new file mode 100644
index 0000000..f7df8cf
--- /dev/null
+++ b/contrib/hstore/expected/nested.out
@@ -0,0 +1,2261 @@
+SELECT 'ff => {a=>12, b=>16}'::hstore;
+          hstore          
+--------------------------
+ "ff"=>{"a"=>12, "b"=>16}
+(1 row)
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123'::hstore;
+               hstore                
+-------------------------------------
+ "ff"=>{"a"=>12, "b"=>16}, "qq"=>123
+(1 row)
+
+SELECT 'aa => {a,aaa}, qq=>{ a=>12, b=>16 , c=> { c1, c2}, d=>{d1=>d1, d2=>d2, d1=>d3} }'::hstore;
+                                             hstore                                             
+------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>12, "b"=>16, "c"=>["c1", "c2"], "d"=>{"d1"=>"d3", "d2"=>"d2"}}
+(1 row)
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+                                               hstore                                               
+----------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>"12", "b"=>"16", "c"=>["c1", "c2"], "d"=>{"d1"=>"d1", "d2"=>"d2"}}
+(1 row)
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+                                                        hstore                                                         
+-----------------------------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>"12", "b"=>"16", "c"=>["c1", "c2", ["c3"], {"c4"=>4}], "d"=>{"d1"=>"d1", "d2"=>"d2"}}
+(1 row)
+
+SELECT 'ff => {a,aaa}'::hstore;
+       hstore       
+--------------------
+ "ff"=>["a", "aaa"]
+(1 row)
+
+select 'null'::hstore;
+ hstore 
+--------
+ NULL
+(1 row)
+
+select '{null}'::hstore;
+ hstore 
+--------
+ [NULL]
+(1 row)
+
+select ''::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+select '{}'::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+--test optional outer braces
+SELECT	'a=>1'::hstore;
+ hstore 
+--------
+ "a"=>1
+(1 row)
+
+SELECT	'{a=>1}'::hstore;
+ hstore 
+--------
+ "a"=>1
+(1 row)
+
+SELECT	'{a,b}'::hstore;
+   hstore   
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT	'{a,{b}}'::hstore;
+    hstore    
+--------------
+ ["a", ["b"]]
+(1 row)
+
+SELECT	'{{a},b}'::hstore;
+    hstore    
+--------------
+ [["a"], "b"]
+(1 row)
+
+SELECT	'{a,{b},{c}}'::hstore;
+       hstore        
+---------------------
+ ["a", ["b"], ["c"]]
+(1 row)
+
+SELECT	'{{a},{b},c}'::hstore;
+       hstore        
+---------------------
+ [["a"], ["b"], "c"]
+(1 row)
+
+SELECT	'{{a},b,{c}}'::hstore;
+       hstore        
+---------------------
+ [["a"], "b", ["c"]]
+(1 row)
+
+SELECT	'{a,{b=>1}}'::hstore;
+     hstore      
+-----------------
+ ["a", {"b"=>1}]
+(1 row)
+
+SELECT	'{{a},{b=>1}}'::hstore;
+      hstore       
+-------------------
+ [["a"], {"b"=>1}]
+(1 row)
+
+SELECT	'a'::hstore;
+ hstore 
+--------
+ "a"
+(1 row)
+
+SELECT	'{a}'::hstore;
+ hstore 
+--------
+ ["a"]
+(1 row)
+
+SELECT	''::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+SELECT	'{}'::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+--nested json
+SELECT	hstore_to_json('a=>1');
+ hstore_to_json 
+----------------
+ {"a": 1}
+(1 row)
+
+SELECT	hstore_to_json('{a=>1}');
+ hstore_to_json 
+----------------
+ {"a": 1}
+(1 row)
+
+SELECT	hstore_to_json('{a,b}');
+ hstore_to_json 
+----------------
+ ["a", "b"]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b}}');
+ hstore_to_json 
+----------------
+ ["a", ["b"]]
+(1 row)
+
+SELECT	hstore_to_json('{{a},b}');
+ hstore_to_json 
+----------------
+ [["a"], "b"]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b},{c}}');
+   hstore_to_json    
+---------------------
+ ["a", ["b"], ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b},c}');
+   hstore_to_json    
+---------------------
+ [["a"], ["b"], "c"]
+(1 row)
+
+SELECT	hstore_to_json('{{a},b,{c}}');
+   hstore_to_json    
+---------------------
+ [["a"], "b", ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b=>1}}');
+ hstore_to_json  
+-----------------
+ ["a", {"b": 1}]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b=>1}}');
+  hstore_to_json   
+-------------------
+ [["a"], {"b": 1}]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b=>1},{c}}');
+      hstore_to_json      
+--------------------------
+ [["a"], {"b": 1}, ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('a');
+ hstore_to_json 
+----------------
+ "a"
+(1 row)
+
+SELECT	hstore_to_json('{a}');
+ hstore_to_json 
+----------------
+ ["a"]
+(1 row)
+
+SELECT	hstore_to_json('');
+ hstore_to_json 
+----------------
+ {}
+(1 row)
+
+SELECT	hstore_to_json('{}');
+ hstore_to_json 
+----------------
+ {}
+(1 row)
+
+SELECT hstore_to_json('"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore);
+                                                     hstore_to_json                                                      
+-------------------------------------------------------------------------------------------------------------------------
+ {"aa": ["a", "aaa"], "qq": {"a": "12", "b": "16", "c": ["c1", "c2", ["c3"], {"c4": 4}], "d": {"d1": "d1", "d2": "d2"}}}
+(1 row)
+
+--
+SELECT 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'ff', 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'qq', 
+	   ('ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'Y') IS NULL AS t, 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'x'; 
+     ?column?     | ?column? | t | ?column? 
+------------------+----------+---+----------
+ "a"=>12, "b"=>16 | 123      | t | [1, 2]
+(1 row)
+
+SELECT '[ a, b, c, d]'::hstore -> 'a';
+ ?column? 
+----------
+ a
+(1 row)
+
+--
+CREATE TABLE testtype (i int, h hstore, a int[], j json);
+INSERT INTO testtype VALUES (1, 'a=>1', '{1,2,3}', '{"x": 2}');
+SELECT populate_record(v, 'i=>2'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""a""=>1","{1,2,3}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, a=>{7,8,9}'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""a""=>1","{7,8,9}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""b""=>3","{7,8,9}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}'::hstore) FROM testtype v;
+                populate_record                
+-----------------------------------------------
+ (2,"""b""=>3","{7,8,9}","{""a"": [1, 2, 3]}")
+(1 row)
+
+--complex delete
+SELECT 'b=>{a,c}'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>1'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT '[2,3,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[a,2,3,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[a,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>1'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT ''::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '{a, 1 , b,2, c,3}'::hstore - ARRAY['d','b'];
+      ?column?       
+---------------------
+ ["a", 1, 2, "c", 3]
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v'::hstore;
+       ?column?        
+-----------------------
+ "a"=>[1, 2], "b"=>"c"
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>23'::hstore;
+       ?column?        
+-----------------------
+ "a"=>[1, 2], "b"=>"c"
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>{1,2}'::hstore;
+            ?column?            
+--------------------------------
+ "a"=>[1, 2], "b"=>"c", "v"=>23
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'a=>{1,2}'::hstore;
+     ?column?      
+-------------------
+ "b"=>"c", "v"=>23
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v'::hstore;
+          ?column?           
+-----------------------------
+ ["a", [1, 2], 23, "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v=>23'::hstore;
+        ?column?         
+-------------------------
+ ["a", [1, 2], "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,23]'::hstore;
+        ?column?         
+-------------------------
+ ["a", [1, 2], "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,{1,2}]'::hstore;
+      ?column?       
+---------------------
+ ["a", 23, "b", "c"]
+(1 row)
+
+--joining
+SELECT 'aa=>1 , b=>2, cq=>3'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+                   ?column?                    
+-----------------------------------------------
+ "1"=>2, "b"=>"g", "aa"=>1, "cq"=>"l", "fg"=>f
+(1 row)
+
+SELECT '{aa,1 , b,2, cq,3}'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+                            ?column?                            
+----------------------------------------------------------------
+ ["aa", 1, "b", 2, "cq", 3, "cq", "l", "b", "g", "fg", f, 1, 2]
+(1 row)
+
+SELECT  'x'::hstore || 'a=>"1"':: hstore;
+    ?column?     
+-----------------
+ ["x", "a", "1"]
+(1 row)
+
+--slice
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
+   slice_array    
+------------------
+ {NULL,NULL,NULL}
+(1 row)
+
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+   slice_array    
+------------------
+ {NULL,NULL,NULL}
+(1 row)
+
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['b','c']);
+ slice_array 
+-------------
+ {2,3}
+(1 row)
+
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+ slice_array 
+-------------
+ {b,c}
+(1 row)
+
+SELECT slice_array(hstore 'aa=>1, b=>{2=>1}, c=>{1,2}', ARRAY['b','c']);
+      slice_array      
+-----------------------
+ {"\"2\"=>1","[1, 2]"}
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['g','h','i']);
+ slice 
+-------
+ 
+(1 row)
+
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+ slice 
+-------
+ 
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['b','c']);
+     slice      
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+   slice    
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>{2=>1}, c=>{1,2}}', ARRAY['b','c']);
+           slice            
+----------------------------
+ "b"=>{"2"=>1}, "c"=>[1, 2]
+(1 row)
+
+--to array
+SELECT %% 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL';
+                ?column?                
+----------------------------------------
+ {b,"[\"a\", \"n\"]",aa,1,cq,l,fg,NULL}
+(1 row)
+
+SELECT %% '{aa,1, cq,l, b,g, fg,NULL}';
+        ?column?         
+-------------------------
+ {aa,1,cq,l,b,g,fg,NULL}
+(1 row)
+
+SELECT hstore_to_matrix( 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL');
+                hstore_to_matrix                
+------------------------------------------------
+ {{b,"[\"a\", \"n\"]"},{aa,1},{cq,l},{fg,NULL}}
+(1 row)
+
+SELECT hstore_to_matrix( '{aa,1, cq,l, b,g, fg,NULL}');
+        hstore_to_matrix         
+---------------------------------
+ {{aa,1},{cq,l},{b,g},{fg,NULL}}
+(1 row)
+
+--contains
+SELECT 'a=>b'::hstore @> 'a=>b, c=>b';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>b'::hstore @> 'a=>b';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{2,1}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1=>2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1=>2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{a,b}'::hstore @> '{a,b, c,b}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{a,b, c,b}'::hstore @> '{a,b}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{a,b, c,{1,2}}'::hstore @> '{a,{1,2}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{a,b, c,{1,2}}'::hstore @> '{b,{1,2}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{3}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{c=>3}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},3}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+-- %>
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'n';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'a';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'b';
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'c';
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd';
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd' %> '1';
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[1,2,3,{a,b}]'::hstore %> '1';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '["1",2,3,{a,b}]'::hstore %> '1';
+ ?column? 
+----------
+ "1"
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 3;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 2;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 1;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 0;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 3;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 2;
+ ?column? 
+----------
+ "c"
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 1;
+ ?column? 
+----------
+ "b"
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 0;
+ ?column? 
+----------
+ "a"
+(1 row)
+
+-- ->
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 3;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 2;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 1;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 0;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 3;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 2;
+ ?column? 
+----------
+ c
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 1;
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 0;
+ ?column? 
+----------
+ a
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -6;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -5;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -4;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -3;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -2;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -1;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -6;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -5;
+ ?column? 
+----------
+ a
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -4;
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -3;
+ ?column? 
+----------
+ c
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -2;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -1;
+ ?column? 
+----------
+ 
+(1 row)
+
+-- #>
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{a}';
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c}';
+ ?column?  
+-----------
+ [1, 2, 3]
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 0}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 1}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 2}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 3}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -1}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -2}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -3}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -4}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{0}';
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{3}';
+ ?column? 
+----------
+ [3, 4]
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4}';
+  ?column?   
+-------------
+ "5"=>"five"
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4,5}';
+ ?column? 
+----------
+ five
+(1 row)
+
+-- #%>
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{a}';
+ ?column? 
+----------
+ "b"
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c}';
+ ?column?  
+-----------
+ [1, 2, 3]
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 0}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 1}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 2}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 3}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -1}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -2}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -3}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -4}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{0}';
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{3}';
+ ?column? 
+----------
+ [3, 4]
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4}';
+  ?column?   
+-------------
+ "5"=>"five"
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4,5}';
+ ?column? 
+----------
+ "five"
+(1 row)
+
+-- ?
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 5;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 0;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 5;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 0;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -6;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -5;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -6;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -5;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{0}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{a}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{b}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 0}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 1}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 2}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 3}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -1}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -2}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -3}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -4}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -5}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{0}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{3}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4,5}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+--deep delete
+SELECT 'a=>1'::hstore #- '{x}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{a}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{NULL}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a}';
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b}';
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c}';
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{x,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{a,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{NULL,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT '[a]'::hstore #- '{2}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a]'::hstore #- '{1}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a]'::hstore #- '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore #- '{-1}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore #- '{-2}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{3}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{2}';
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{1}';
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{0}';
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-1}';
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-2}';
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-3}';
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-4}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{0,0}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{x}';
+                             ?column?                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{a}';
+                         ?column?                          
+-----------------------------------------------------------
+ "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b}';
+                       ?column?                       
+------------------------------------------------------
+ "a"=>1, "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c}';
+                      ?column?                      
+----------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d}';
+                   ?column?                    
+-----------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, 0}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}' #- '{b, -1}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>NULL, "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 1}';
+                           ?column?                            
+---------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>NULL, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 2}';
+                             ?column?                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, -2}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 1}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>NULL}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}' #- '{d, 1, 0}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>NULL}, "n"=>NULL
+(1 row)
+
+-- delete(int)
+SELECT '[a,b,c]'::hstore - 3;
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 2;
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 1;
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 0;
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -1;
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -2;
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -3;
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -4;
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 3;
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 2;
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 1;
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 0;
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -1;
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -2;
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -3;
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -4;
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+--replace
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{1,2,3}');
+                                replace                                 
+------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>[1, 2, 3]
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b,-1}', '{1,2,3}');
+                                  replace                                  
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, [1, 2, 3]], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1,0}', '{1,2,3}');
+                                  replace                                  
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[[1, 2, 3], 3]}, "n"=>NULL
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,NULL,0}', '{1,2,3}');
+                              replace                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+--deep concat
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'n=>not_null');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>"not_null"
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'n=>not_null');
+                                  concat_path                                   
+--------------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>{"n"=>"not_null"}
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'not_null');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>"not_null"
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{not_null}');
+                                concat_path                                
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>["not_null"]
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'b=>{3,4}');
+                            concat_path                            
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[3, 4], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b}', '{3,4}');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2, 3, 4], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '{4,5}');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3, 4, 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '4=>5');
+                                concat_path                                
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3, "4", 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d}', '2=>{4,5}');
+                                  concat_path                                   
+--------------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3], "2"=>[4, 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{NULL,1}', '4=>5');
+                            concat_path                            
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('x'::hstore, '{}'::text[], 'a=>"1"':: hstore);
+   concat_path   
+-----------------
+ ["x", "a", "1"]
+(1 row)
+
+--cast 
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::text)::hstore AS err;
+ERROR:  bad hstore representation
+DETAIL:  syntax error, unexpected STRING_P, expecting '}' or ',' at end of input
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json)::hstore AS ok;
+                         ok                         
+----------------------------------------------------
+ "f2"=>{"f3"=>1}, "f4"=>{"f5"=>99, "f6"=>"stringy"}
+(1 row)
+
+--hvals
+SELECT q->'tags' FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore) AS q;
+ ?column? 
+----------
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+                      q                       
+----------------------------------------------
+ [{"sh"=>2, "tags"=>1}, {"sh"=>4, "tags"=>3}]
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+ "sh"=>4, "tags"=>3
+(2 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+ q 
+---
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+                                q                                
+-----------------------------------------------------------------
+ "1"=>"first", "a"=>{"b"=>"c", "c"=>"b"}, "b"=>[1, 2], "c"=>"cc"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+    q    
+---------
+ "first"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+         q          
+--------------------
+ "b"=>"c", "c"=>"b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+   q    
+--------
+ [1, 2]
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+  q   
+------
+ "cc"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "first"
+ "b"=>"c", "c"=>"b"
+ [1, 2]
+ "cc"
+(4 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+  q  
+-----
+ "b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+  q  
+-----
+ "b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+ q 
+---
+ 1
+ 2
+(2 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+ q 
+---
+ 2
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+    q    
+---------
+ "first"
+ 2
+(2 rows)
+
+--svals path
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+                      q                       
+----------------------------------------------
+ [{"sh"=>2, "tags"=>1}, {"sh"=>4, "tags"=>3}]
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+ "sh"=>4, "tags"=>3
+(2 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+ q 
+---
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+                                q                                
+-----------------------------------------------------------------
+ "1"=>"first", "a"=>{"b"=>"c", "c"=>"b"}, "b"=>[1, 2], "c"=>"cc"
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+   q   
+-------
+ first
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+         q          
+--------------------
+ "b"=>"c", "c"=>"b"
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+   q    
+--------
+ [1, 2]
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+ q  
+----
+ cc
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ first
+ "b"=>"c", "c"=>"b"
+ [1, 2]
+ cc
+(4 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+ q 
+---
+ b
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+ q 
+---
+ b
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+ q 
+---
+ 1
+ 2
+(2 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+ q 
+---
+ 2
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+   q   
+-------
+ first
+ 2
+(2 rows)
+
+--each
+SELECT * FROM each('a=>b, c=>cc'::hstore) AS q;
+ key | value 
+-----+-------
+ a   | b
+ c   | cc
+(2 rows)
+
+SELECT * FROM each('[a, b, c, cc]'::hstore) AS q;
+ key | value 
+-----+-------
+     | a
+     | b
+     | c
+     | cc
+(4 rows)
+
+SELECT * FROM each('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+ key |              value               
+-----+----------------------------------
+ 1   | first
+ a   | "1"=>"first", "b"=>"c", "c"=>"b"
+ b   | [1, 2]
+ c   | cc
+ n   | 
+(5 rows)
+
+SELECT * FROM each_hstore('a=>b, c=>cc'::hstore) AS q;
+ key | value 
+-----+-------
+ a   | "b"
+ c   | "cc"
+(2 rows)
+
+SELECT * FROM each_hstore('[a, b, c, cc]'::hstore) AS q;
+ key | value 
+-----+-------
+     | "a"
+     | "b"
+     | "c"
+     | "cc"
+(4 rows)
+
+SELECT * FROM each_hstore('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+ key |              value               
+-----+----------------------------------
+ 1   | "first"
+ a   | "1"=>"first", "b"=>"c", "c"=>"b"
+ b   | [1, 2]
+ c   | "cc"
+ n   | 
+(5 rows)
+
+--decoration
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]]'::hstore AS h, '[a, {b=>c}, [c, d, e]]'::hstore AS a;
+                  h                   |                 a                  
+--------------------------------------+------------------------------------
+ "a"=>1, "b"=>{"c"=>3}, "d"=>[4, [5]] | ["a", {"b"=>"c"}, ["c", "d", "e"]]
+(1 row)
+
+SET hstore.pretty_print = true;
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]], e=>[1,2,3,4], f=>g, g=>j'::hstore AS h, 
+	   '[a, {b=>c, c=>d}, [c, d, e, [1,2], h, {f=>g, g=>f}]]'::hstore AS a;
+     h      |           a            
+------------+------------------------
+ "a"=>1,   +| [                     +
+ "b"=>     +|     "a",              +
+ {         +|     {                 +
+     "c"=>3+|         "b"=>"c",     +
+ },        +|         "c"=>"d"      +
+ "d"=>     +|     },                +
+ [         +|     [                 +
+     4,    +|         "c",          +
+     [     +|         "d",          +
+         5 +|         "e",          +
+     ]     +|         [             +
+ ],        +|             1,        +
+ "e"=>     +|             2         +
+ [         +|         ],            +
+     1,    +|         "h",          +
+     2,    +|         {             +
+     3,    +|             "f"=>"g", +
+     4     +|             "g"=>f    +
+ ],        +|         }             +
+ "f"=>"g", +|     ]                 +
+ "g"=>"j"   | ]
+(1 row)
+
+RESET hstore.pretty_print;
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore);
+                             hstore_print                              
+-----------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>[1, 2, 3, "3", "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true );
+                           hstore_print                            
+-------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>[1, 2, 3, 3, "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true );
+                              hstore_print                               
+-------------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>[1, 2, 3, "3", "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, array_curly_braces := true );
+                             hstore_print                              
+-----------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>{1, 2, 3, "3", "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": "f", "123": "string", "arr": [1, 2, 3, "3", "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, loose := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": false, "123": "string", "arr": [1, 2, 3, 3, "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, root_hash_decorated := true );
+                                 hstore_print                                  
+-------------------------------------------------------------------------------
+ {"a": true, "f": true, "t": "f", "123": "string", "arr": [1, 2, 3, "3", "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, array_curly_braces := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": "f", "123": "string", "arr": {1, 2, 3, "3", "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, root_hash_decorated := true );
+                            hstore_print                             
+---------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>[1, 2, 3, 3, "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, array_curly_braces := true );
+                           hstore_print                            
+-------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>{1, 2, 3, 3, "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true );
+                              hstore_print                               
+-------------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>{1, 2, 3, "3", "x"}}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true, loose := true);
+                            hstore_print                             
+---------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>{1, 2, 3, 3, "x"}}
+(1 row)
+
diff --git a/contrib/hstore/expected/types.out b/contrib/hstore/expected/types.out
new file mode 100644
index 0000000..4206c58
--- /dev/null
+++ b/contrib/hstore/expected/types.out
@@ -0,0 +1,762 @@
+SELECT '"foo"=>true'::hstore;
+  hstore  
+----------
+ "foo"=>t
+(1 row)
+
+SELECT 'foo=>true'::hstore;
+  hstore  
+----------
+ "foo"=>t
+(1 row)
+
+SELECT '"true"=>true'::hstore;
+  hstore   
+-----------
+ "true"=>t
+(1 row)
+
+SELECT 'true=>true'::hstore;
+  hstore   
+-----------
+ "true"=>t
+(1 row)
+
+SELECT '"t"=>true'::hstore;
+ hstore 
+--------
+ "t"=>t
+(1 row)
+
+SELECT 't=>true'::hstore;
+ hstore 
+--------
+ "t"=>t
+(1 row)
+
+SELECT '"false"=>true'::hstore;
+   hstore   
+------------
+ "false"=>t
+(1 row)
+
+SELECT 'false=>true'::hstore;
+   hstore   
+------------
+ "false"=>t
+(1 row)
+
+SELECT '"f"=>true'::hstore;
+ hstore 
+--------
+ "f"=>t
+(1 row)
+
+SELECT 'f=>true'::hstore;
+ hstore 
+--------
+ "f"=>t
+(1 row)
+
+SELECT '"foo"=>false'::hstore;
+  hstore  
+----------
+ "foo"=>f
+(1 row)
+
+SELECT 'foo=>false'::hstore;
+  hstore  
+----------
+ "foo"=>f
+(1 row)
+
+SELECT '"false"=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT 'false=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT '"t"=>false'::hstore;
+ hstore 
+--------
+ "t"=>f
+(1 row)
+
+SELECT 't=>false'::hstore;
+ hstore 
+--------
+ "t"=>f
+(1 row)
+
+SELECT '"false"=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT 'false=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT '"f"=>false'::hstore;
+ hstore 
+--------
+ "f"=>f
+(1 row)
+
+SELECT 'f=>false'::hstore;
+ hstore 
+--------
+ "f"=>f
+(1 row)
+
+SELECT '"1"=>x'::hstore;
+  hstore  
+----------
+ "1"=>"x"
+(1 row)
+
+SELECT '1=>x'::hstore;
+  hstore  
+----------
+ "1"=>"x"
+(1 row)
+
+SELECT 'foo=>1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>1.'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>1.0'::hstore;
+   hstore   
+------------
+ "foo"=>1.0
+(1 row)
+
+SELECT 'foo=>1.01'::hstore;
+   hstore    
+-------------
+ "foo"=>1.01
+(1 row)
+
+SELECT 'foo=>1.01e'::hstore;
+     hstore     
+----------------
+ "foo"=>"1.01e"
+(1 row)
+
+SELECT 'foo=>1.01e1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>1.01e+1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>1.01e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>0.101
+(1 row)
+
+SELECT 'foo=>.1'::hstore;
+   hstore   
+------------
+ "foo"=>0.1
+(1 row)
+
+SELECT 'foo=>.1e'::hstore;
+    hstore    
+--------------
+ "foo"=>".1e"
+(1 row)
+
+SELECT 'foo=>.1e1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>.1e+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>0.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>00.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+1.'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+1.0'::hstore;
+   hstore   
+------------
+ "foo"=>1.0
+(1 row)
+
+SELECT 'foo=>+1.01'::hstore;
+   hstore    
+-------------
+ "foo"=>1.01
+(1 row)
+
+SELECT 'foo=>+1.01e'::hstore;
+     hstore      
+-----------------
+ "foo"=>"+1.01e"
+(1 row)
+
+SELECT 'foo=>+1.01e1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>+1.01e+1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>+1.01e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>0.101
+(1 row)
+
+SELECT 'foo=>+.1'::hstore;
+   hstore   
+------------
+ "foo"=>0.1
+(1 row)
+
+SELECT 'foo=>+.1e'::hstore;
+    hstore     
+---------------
+ "foo"=>"+.1e"
+(1 row)
+
+SELECT 'foo=>+.1e1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+.1e+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>-1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-1.'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-1.0'::hstore;
+   hstore    
+-------------
+ "foo"=>-1.0
+(1 row)
+
+SELECT 'foo=>-1.01'::hstore;
+    hstore    
+--------------
+ "foo"=>-1.01
+(1 row)
+
+SELECT 'foo=>-1.01e'::hstore;
+     hstore      
+-----------------
+ "foo"=>"-1.01e"
+(1 row)
+
+SELECT 'foo=>-1.01e1'::hstore;
+    hstore    
+--------------
+ "foo"=>-10.1
+(1 row)
+
+SELECT 'foo=>-1.01e+1'::hstore;
+    hstore    
+--------------
+ "foo"=>-10.1
+(1 row)
+
+SELECT 'foo=>-1.01e-1'::hstore;
+    hstore     
+---------------
+ "foo"=>-0.101
+(1 row)
+
+SELECT 'foo=>-.1'::hstore;
+   hstore    
+-------------
+ "foo"=>-0.1
+(1 row)
+
+SELECT 'foo=>-.1e'::hstore;
+    hstore     
+---------------
+ "foo"=>"-.1e"
+(1 row)
+
+SELECT 'foo=>-.1e1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-.1e+1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-.1e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>-0.01
+(1 row)
+
+SELECT 'foo=>1e2000'::hstore;
+     hstore      
+-----------------
+ "foo"=>"1e2000"
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'foo';
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'bar';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 0;
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 1;
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'foo';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'bar';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 0;
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 1;
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 0}';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 1}';
+    ?column?    
+----------------
+ 0.000000000001
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'foo';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'foo';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'foo';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 1;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'foo';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 1;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 1}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT hstore_typeof('a=>b') AS hash;
+ hash 
+------
+ hash
+(1 row)
+
+SELECT hstore_typeof('{a=>b}') AS hash;
+ hash 
+------
+ hash
+(1 row)
+
+SELECT hstore_typeof('{a, b}') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('{{a=>b}}') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('[a, b]') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('') AS "NULL";
+ NULL 
+------
+ 
+(1 row)
+
+SELECT hstore_typeof('NULL') AS "null";
+ null 
+------
+ null
+(1 row)
+
+SELECT hstore_typeof('1.0') AS numeric;
+ numeric 
+---------
+ numeric
+(1 row)
+
+SELECT hstore_typeof('t') AS bool;
+ bool 
+------
+ bool
+(1 row)
+
+SELECT hstore_typeof('f') AS bool;
+ bool 
+------
+ bool
+(1 row)
+
+SELECT hstore('xxx', 't'::bool);
+  hstore  
+----------
+ "xxx"=>t
+(1 row)
+
+SELECT hstore('xxx', 'f'::bool);
+  hstore  
+----------
+ "xxx"=>f
+(1 row)
+
+SELECT hstore('xxx', 3.14);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore('xxx', 3.14::numeric);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore('xxx', '3.14'::numeric);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore(NULL);
+ hstore 
+--------
+ 
+(1 row)
+
+SELECT hstore('NULL');
+ hstore 
+--------
+ NULL
+(1 row)
+
+SELECT hstore('t'::bool) AS "true", hstore('f'::bool) AS "false";
+ true | false 
+------+-------
+ t    | f
+(1 row)
+
+SELECT hstore(3.14), hstore(3.14::numeric), hstore('3.14'::numeric);
+ hstore | hstore | hstore 
+--------+--------+--------
+ 3.14   | 3.14   | 3.14
+(1 row)
+
+SELECT hstore('xxx', 'foo=>t, bar=>3.14, zzz=>xxx'::hstore);
+                    hstore                    
+----------------------------------------------
+ "xxx"=>{"bar"=>3.14, "foo"=>t, "zzz"=>"xxx"}
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int2[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int4[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int8[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float4[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float8[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,f},{f,t,NULL}}'::bool[]);
+      array_to_hstore      
+---------------------------
+ [[t, t, f], [f, t, NULL]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::text[]);
+           array_to_hstore           
+-------------------------------------
+ [["1", "1", "4"], ["23", "3", "5"]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::varchar[]);
+           array_to_hstore           
+-------------------------------------
+ [["1", "1", "4"], ["23", "3", "5"]]
+(1 row)
+
+SELECT array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]);
+                       array_to_hstore                       
+-------------------------------------------------------------
+ [[[1, 11], [1, 1], [4, 41], [23, 231]], [[3, 31], [5, 51]]]
+(1 row)
+
+SELECT hstore('array', array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]));
+                                hstore                                
+----------------------------------------------------------------------
+ "array"=>[[[1, 11], [1, 1], [4, 41], [23, 231]], [[3, 31], [5, 51]]]
+(1 row)
+
+SELECT 'a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore;
+                        hstore                         
+-------------------------------------------------------
+ "a"=>"00012333", "b"=>"12233", "c"=>12333, "d"=>12233
+(1 row)
+
+SELECT hstore_to_json('a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore);
+                     hstore_to_json                      
+---------------------------------------------------------
+ {"a": "00012333", "b": "12233", "c": 12333, "d": 12233}
+(1 row)
+
+SELECT hstore_to_json_loose('a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore);
+                 hstore_to_json_loose                  
+-------------------------------------------------------
+ {"a": "00012333", "b": 12233, "c": 12333, "d": 12233}
+(1 row)
+
diff --git a/contrib/hstore/hstore--1.2--1.3.sql b/contrib/hstore/hstore--1.2--1.3.sql
new file mode 100644
index 0000000..b913a08
--- /dev/null
+++ b/contrib/hstore/hstore--1.2--1.3.sql
@@ -0,0 +1,338 @@
+/* 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 FUNCTION hstore2jsonb(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore2jsonb'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS jsonb)
+  WITH FUNCTION hstore2jsonb(hstore);
+
+CREATE FUNCTION jsonb2hstore(jsonb)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'jsonb2hstore'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (jsonb AS hstore)
+  WITH FUNCTION jsonb2hstore(jsonb);
+
diff --git a/contrib/hstore/hstore--1.2.sql b/contrib/hstore/hstore--1.2.sql
deleted file mode 100644
index f415a72..0000000
--- a/contrib/hstore/hstore--1.2.sql
+++ /dev/null
@@ -1,537 +0,0 @@
-/* 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
-
-CREATE TYPE hstore;
-
-CREATE FUNCTION hstore_in(cstring)
-RETURNS hstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_out(hstore)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_recv(internal)
-RETURNS hstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_send(hstore)
-RETURNS bytea
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE TYPE hstore (
-        INTERNALLENGTH = -1,
-        INPUT = hstore_in,
-        OUTPUT = hstore_out,
-        RECEIVE = hstore_recv,
-        SEND = hstore_send,
-        STORAGE = extended
-);
-
-CREATE FUNCTION hstore_version_diag(hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_version_diag'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION fetchval(hstore,text)
-RETURNS text
-AS 'MODULE_PATHNAME','hstore_fetchval'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR -> (
-	LEFTARG = hstore,
-	RIGHTARG = text,
-	PROCEDURE = fetchval
-);
-
-CREATE FUNCTION slice_array(hstore,text[])
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_slice_to_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR -> (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = slice_array
-);
-
-CREATE FUNCTION slice(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_slice_to_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION isexists(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION exist(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists'
-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'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?| (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = exists_any,
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION exists_all(hstore,text[])
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_all'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?& (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = exists_all,
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION isdefined(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_defined'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION defined(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_defined'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = text,
-	PROCEDURE = delete
-);
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = delete
-);
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = delete
-);
-
-CREATE FUNCTION hs_concat(hstore,hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_concat'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR || (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_concat
-);
-
-CREATE FUNCTION hs_contains(hstore,hstore)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_contains'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hs_contained(hstore,hstore)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_contained'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR @> (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contains,
-	COMMUTATOR = '<@',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE OPERATOR <@ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contained,
-	COMMUTATOR = '@>',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
--- obsolete:
-CREATE OPERATOR @ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contains,
-	COMMUTATOR = '~',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contained,
-	COMMUTATOR = '@',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION tconvert(text,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_text'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_text'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text[],text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_arrays'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null)
-
-CREATE FUNCTION hstore(text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_array'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE CAST (text[] AS hstore)
-  WITH FUNCTION hstore(text[]);
-
-CREATE FUNCTION hstore_to_json(hstore)
-RETURNS json
-AS 'MODULE_PATHNAME', 'hstore_to_json'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE CAST (hstore AS json)
-  WITH FUNCTION hstore_to_json(hstore);
-
-CREATE FUNCTION hstore_to_json_loose(hstore)
-RETURNS json
-AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION hstore(record)
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_record'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype)
-
-CREATE FUNCTION hstore_to_array(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_to_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %% (
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_to_array
-);
-
-CREATE FUNCTION hstore_to_matrix(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_to_matrix'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %# (
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_to_matrix
-);
-
-CREATE FUNCTION akeys(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_akeys'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION avals(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_avals'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION skeys(hstore)
-RETURNS setof text
-AS 'MODULE_PATHNAME','hstore_skeys'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION svals(hstore)
-RETURNS setof text
-AS 'MODULE_PATHNAME','hstore_svals'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION each(IN hs hstore,
-    OUT key text,
-    OUT value text)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME','hstore_each'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION populate_record(anyelement,hstore)
-RETURNS anyelement
-AS 'MODULE_PATHNAME', 'hstore_populate_record'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore)
-
-CREATE OPERATOR #= (
-	LEFTARG = anyelement,
-	RIGHTARG = hstore,
-	PROCEDURE = populate_record
-);
-
--- btree support
-
-CREATE FUNCTION hstore_eq(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_eq'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_ne(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_ne'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_gt(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_gt'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_ge(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_ge'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_lt(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_lt'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_le(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_le'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_cmp(hstore,hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_cmp'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR = (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_eq,
-       COMMUTATOR = =,
-       NEGATOR = <>,
-       RESTRICT = eqsel,
-       JOIN = eqjoinsel,
-       MERGES,
-       HASHES
-);
-CREATE OPERATOR <> (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_ne,
-       COMMUTATOR = <>,
-       NEGATOR = =,
-       RESTRICT = neqsel,
-       JOIN = neqjoinsel
-);
-
--- the comparison operators have funky names (and are undocumented)
--- in an attempt to discourage anyone from actually using them. they
--- only exist to support the btree opclass
-
-CREATE OPERATOR #<# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_lt,
-       COMMUTATOR = #>#,
-       NEGATOR = #>=#,
-       RESTRICT = scalarltsel,
-       JOIN = scalarltjoinsel
-);
-CREATE OPERATOR #<=# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_le,
-       COMMUTATOR = #>=#,
-       NEGATOR = #>#,
-       RESTRICT = scalarltsel,
-       JOIN = scalarltjoinsel
-);
-CREATE OPERATOR #># (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_gt,
-       COMMUTATOR = #<#,
-       NEGATOR = #<=#,
-       RESTRICT = scalargtsel,
-       JOIN = scalargtjoinsel
-);
-CREATE OPERATOR #>=# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_ge,
-       COMMUTATOR = #<=#,
-       NEGATOR = #<#,
-       RESTRICT = scalargtsel,
-       JOIN = scalargtjoinsel
-);
-
-CREATE OPERATOR CLASS btree_hstore_ops
-DEFAULT FOR TYPE hstore USING btree
-AS
-	OPERATOR	1	#<# ,
-	OPERATOR	2	#<=# ,
-	OPERATOR	3	= ,
-	OPERATOR	4	#>=# ,
-	OPERATOR	5	#># ,
-	FUNCTION	1	hstore_cmp(hstore,hstore);
-
--- hash support
-
-CREATE FUNCTION hstore_hash(hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_hash'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR CLASS hash_hstore_ops
-DEFAULT FOR TYPE hstore USING hash
-AS
-	OPERATOR	1	= ,
-	FUNCTION	1	hstore_hash(hstore);
-
--- GiST support
-
-CREATE TYPE ghstore;
-
-CREATE FUNCTION ghstore_in(cstring)
-RETURNS ghstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION ghstore_out(ghstore)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE TYPE ghstore (
-        INTERNALLENGTH = -1,
-        INPUT = ghstore_in,
-        OUTPUT = ghstore_out
-);
-
-CREATE FUNCTION ghstore_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_union(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_same(internal, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR CLASS gist_hstore_ops
-DEFAULT FOR TYPE hstore USING gist
-AS
-	OPERATOR        7       @> ,
-	OPERATOR        9       ?(hstore,text) ,
-	OPERATOR        10      ?|(hstore,text[]) ,
-	OPERATOR        11      ?&(hstore,text[]) ,
-        --OPERATOR        8       <@ ,
-        OPERATOR        13      @ ,
-        --OPERATOR        14      ~ ,
-        FUNCTION        1       ghstore_consistent (internal, internal, int, oid, internal),
-        FUNCTION        2       ghstore_union (internal, internal),
-        FUNCTION        3       ghstore_compress (internal),
-        FUNCTION        4       ghstore_decompress (internal),
-        FUNCTION        5       ghstore_penalty (internal, internal, internal),
-        FUNCTION        6       ghstore_picksplit (internal, internal),
-        FUNCTION        7       ghstore_same (internal, internal, internal),
-        STORAGE         ghstore;
-
--- GIN support
-
-CREATE FUNCTION gin_extract_hstore(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR CLASS gin_hstore_ops
-DEFAULT FOR TYPE hstore USING gin
-AS
-	OPERATOR        7       @>,
-	OPERATOR        9       ?(hstore,text),
-	OPERATOR        10      ?|(hstore,text[]),
-	OPERATOR        11      ?&(hstore,text[]),
-	FUNCTION        1       bttextcmp(text,text),
-	FUNCTION        2       gin_extract_hstore(internal, internal),
-	FUNCTION        3       gin_extract_hstore_query(internal, internal, int2, internal, internal),
-	FUNCTION        4       gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
-	STORAGE         text;
diff --git a/contrib/hstore/hstore--1.3.sql b/contrib/hstore/hstore--1.3.sql
new file mode 100644
index 0000000..153cb74
--- /dev/null
+++ b/contrib/hstore/hstore--1.3.sql
@@ -0,0 +1,854 @@
+/* contrib/hstore/hstore--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION hstore" to load this file. \quit
+
+CREATE TYPE hstore;
+
+CREATE FUNCTION hstore_in(cstring)
+RETURNS hstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_out(hstore)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_recv(internal)
+RETURNS hstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_send(hstore)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE hstore (
+        INTERNALLENGTH = -1,
+        INPUT = hstore_in,
+        OUTPUT = hstore_out,
+        RECEIVE = hstore_recv,
+        SEND = hstore_send,
+        STORAGE = extended
+);
+
+CREATE FUNCTION hstore_version_diag(hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_version_diag'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION fetchval(hstore,text)
+RETURNS text
+AS 'MODULE_PATHNAME','hstore_fetchval'
+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_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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR -> (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = slice_array
+);
+
+CREATE FUNCTION slice(hstore,text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_slice_to_hstore'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION isexists(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION exist(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ? (
+	LEFTARG = hstore,
+	RIGHTARG = text,
+	PROCEDURE = exist,
+	RESTRICT = contsel,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ?| (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = exists_any,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION exists_all(hstore,text[])
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists_all'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ?& (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = exists_all,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION isdefined(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_defined'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION defined(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_defined'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,text)
+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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,hstore)
+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[],
+	PROCEDURE = delete
+);
+
+CREATE OPERATOR - (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR || (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hs_contained(hstore,hstore)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_contained'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR @> (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contains,
+	COMMUTATOR = '<@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contained,
+	COMMUTATOR = '@>',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+-- obsolete:
+CREATE OPERATOR @ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contains,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contained,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION tconvert(text,text)
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_from_text'
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
+
+CREATE FUNCTION hstore(text,text)
+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'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null)
+
+CREATE FUNCTION hstore(text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_array'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (text[] AS hstore)
+  WITH FUNCTION hstore(text[]);
+
+CREATE FUNCTION hstore_to_json(hstore)
+RETURNS json
+AS 'MODULE_PATHNAME', 'hstore_to_json'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS json)
+  WITH FUNCTION hstore_to_json(hstore);
+
+CREATE FUNCTION hstore2jsonb(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore2jsonb'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS jsonb)
+  WITH FUNCTION hstore2jsonb(hstore);
+
+CREATE FUNCTION jsonb2hstore(jsonb)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'jsonb2hstore'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (jsonb AS hstore)
+  WITH FUNCTION jsonb2hstore(jsonb);
+
+CREATE FUNCTION hstore_to_json_loose(hstore)
+RETURNS json
+AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION hstore(record)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_record'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype)
+
+CREATE FUNCTION hstore_to_array(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_to_array'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR %% (
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_to_array
+);
+
+CREATE FUNCTION hstore_to_matrix(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_to_matrix'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR %# (
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_to_matrix
+);
+
+CREATE FUNCTION akeys(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_akeys'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION avals(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_avals'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION skeys(hstore)
+RETURNS setof text
+AS 'MODULE_PATHNAME','hstore_skeys'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION svals(hstore)
+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)
+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'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore)
+
+CREATE OPERATOR #= (
+	LEFTARG = anyelement,
+	RIGHTARG = hstore,
+	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)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_eq'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_ne(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_ne'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_gt(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_gt'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_ge(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_ge'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_lt(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_lt'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_le(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_le'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_cmp(hstore,hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_cmp'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR = (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_eq,
+       COMMUTATOR = =,
+       NEGATOR = <>,
+       RESTRICT = eqsel,
+       JOIN = eqjoinsel,
+       MERGES,
+       HASHES
+);
+CREATE OPERATOR <> (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_ne,
+       COMMUTATOR = <>,
+       NEGATOR = =,
+       RESTRICT = neqsel,
+       JOIN = neqjoinsel
+);
+
+-- the comparison operators have funky names (and are undocumented)
+-- in an attempt to discourage anyone from actually using them. they
+-- only exist to support the btree opclass
+
+CREATE OPERATOR #<# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_lt,
+       COMMUTATOR = #>#,
+       NEGATOR = #>=#,
+       RESTRICT = scalarltsel,
+       JOIN = scalarltjoinsel
+);
+CREATE OPERATOR #<=# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_le,
+       COMMUTATOR = #>=#,
+       NEGATOR = #>#,
+       RESTRICT = scalarltsel,
+       JOIN = scalarltjoinsel
+);
+CREATE OPERATOR #># (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_gt,
+       COMMUTATOR = #<#,
+       NEGATOR = #<=#,
+       RESTRICT = scalargtsel,
+       JOIN = scalargtjoinsel
+);
+CREATE OPERATOR #>=# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_ge,
+       COMMUTATOR = #<=#,
+       NEGATOR = #<#,
+       RESTRICT = scalargtsel,
+       JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR CLASS btree_hstore_ops
+DEFAULT FOR TYPE hstore USING btree
+AS
+	OPERATOR	1	#<# ,
+	OPERATOR	2	#<=# ,
+	OPERATOR	3	= ,
+	OPERATOR	4	#>=# ,
+	OPERATOR	5	#># ,
+	FUNCTION	1	hstore_cmp(hstore,hstore);
+
+-- hash support
+
+CREATE FUNCTION hstore_hash(hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_hash'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR CLASS hash_hstore_ops
+DEFAULT FOR TYPE hstore USING hash
+AS
+	OPERATOR	1	= ,
+	FUNCTION	1	hstore_hash(hstore);
+
+-- GiST support
+
+CREATE TYPE ghstore;
+
+CREATE FUNCTION ghstore_in(cstring)
+RETURNS ghstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION ghstore_out(ghstore)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE ghstore (
+        INTERNALLENGTH = -1,
+        INPUT = ghstore_in,
+        OUTPUT = ghstore_out
+);
+
+CREATE FUNCTION ghstore_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_union(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_same(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR CLASS gist_hstore_ops
+DEFAULT FOR TYPE hstore USING gist
+AS
+	OPERATOR        7       @> ,
+	OPERATOR        9       ?(hstore,text) ,
+	OPERATOR        10      ?|(hstore,text[]) ,
+	OPERATOR        11      ?&(hstore,text[]) ,
+        --OPERATOR        8       <@ ,
+        OPERATOR        13      @ ,
+        --OPERATOR        14      ~ ,
+        FUNCTION        1       ghstore_consistent (internal, internal, int, oid, internal),
+        FUNCTION        2       ghstore_union (internal, internal),
+        FUNCTION        3       ghstore_compress (internal),
+        FUNCTION        4       ghstore_decompress (internal),
+        FUNCTION        5       ghstore_penalty (internal, internal, internal),
+        FUNCTION        6       ghstore_picksplit (internal, internal),
+        FUNCTION        7       ghstore_same (internal, internal, internal),
+        STORAGE         ghstore;
+
+-- GIN support: default opclass
+
+CREATE FUNCTION gin_extract_hstore(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR CLASS gin_hstore_ops
+DEFAULT FOR TYPE hstore USING gin
+AS
+	OPERATOR        7       @>,
+	OPERATOR        9       ?(hstore,text),
+	OPERATOR        10      ?|(hstore,text[]),
+	OPERATOR        11      ?&(hstore,text[]),
+	FUNCTION        1       bttextcmp(text,text),
+	FUNCTION        2       gin_extract_hstore(internal, internal),
+	FUNCTION        3       gin_extract_hstore_query(internal, internal, int2, internal, internal),
+	FUNCTION        4       gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
+	STORAGE         text;
+
+-- 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;
+
+
+
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index 9daf5e2..dcc3b68 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,5 +1,5 @@
 # hstore extension
 comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/hstore'
 relocatable = true
diff --git a/contrib/hstore/hstore.h b/contrib/hstore/hstore.h
index 23c8a6f..a66b435 100644
--- a/contrib/hstore/hstore.h
+++ b/contrib/hstore/hstore.h
@@ -4,9 +4,7 @@
 #ifndef __HSTORE_H__
 #define __HSTORE_H__
 
-#include "fmgr.h"
-#include "utils/array.h"
-
+#include "utils/jsonb.h"
 
 /*
  * HEntry: there is one of these for each key _and_ value in an hstore
@@ -15,158 +13,128 @@
  * by subtraction from the previous entry.	the ISFIRST flag lets us tell
  * whether there is a previous entry.
  */
-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]))
+
+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_ISCALAR is only used in send/recv */
+#define HENTRY_ISHASH		JENTRY_ISOBJECT
+#define HENTRY_ISARRAY		JENTRY_ISARRAY
+#define HENTRY_ISCALAR		JENTRY_ISCALAR
+
+#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_)
 
 /*
- * 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
+ * determined by the size of "endpos" (ie HENTRY_POSMASK)
  */
-#define HSTORE_MAX_KEY_LEN 0x3FFFFFFF
-#define HSTORE_MAX_VALUE_LEN 0x3FFFFFFF
+#define HSTORE_MAX_KEY_LEN 		HENTRY_POSMASK
+#define HSTORE_MAX_VALUE_LEN 	HENTRY_POSMASK
 
-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;
+typedef Jsonb HStore;
 
 /*
  * it's not possible to get more than 2^28 items into an hstore,
  * so we reserve the top few bits of the size field. See hstore_compat.c
  * for one reason why.	Some bits are left for future use here.
  */
-#define HS_FLAG_NEWVERSION 0x80000000
+#define HS_FLAG_NEWVERSION 		0x80000000
+#define HS_FLAG_ARRAY			JB_FLAG_ARRAY
+#define HS_FLAG_HASH			JB_FLAG_OBJECT
+#define HS_FLAG_SCALAR			JB_FLAG_SCALAR
 
-#define HS_COUNT(hsp_) ((hsp_)->size_ & 0x0FFFFFFF)
-#define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION)
+#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 HSHRDSIZE	(sizeof(HStore))
-#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) )
+/* DatumGetHStoreP includes support for reading old-format hstore values */
+extern HStore *hstoreUpgrade(Datum orig);
 
-/* note multiple evaluations of x */
-#define ARRPTR(x)		( (HEntry*) ( (HStore*)(x) + 1 ) )
-#define STRPTR(x)		( (char*)(ARRPTR(x) + HS_COUNT((HStore*)(x)) * 2) )
+#define DatumGetHStoreP(d) hstoreUpgrade(d)
 
-/* 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 PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x))
 
-/*
- * 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.
- */
+typedef JsonbPair HStorePair;
+typedef JsonbValue HStoreValue;
 
-/*
- * 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)
+/* JsonbValue.type renaming */
+#define hsvNull		jbvNull
+#define hsvString	jbvString
+#define hsvNumeric	jbvNumeric
+#define hsvBool		jbvBool
+#define hsvArray	jbvArray
+#define hsvHash		jbvHash
+#define hsvBinary	jbvBinary
 
 /*
- * add one key/item pair, from a Pairs structure, into an
- * under-construction hstore
+ * hstore support functions, they are mostly the same as jsonb
  */
-#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)
 
-/* DatumGetHStoreP includes support for reading old-format hstore values */
-extern HStore *hstoreUpgrade(Datum orig);
+#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 DatumGetHStoreP(d) hstoreUpgrade(d)
+#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))
 
-#define PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x))
+#define compareHStoreBinaryValue(a, b)				compareJsonbBinaryValue((a), (b))
+#define compareHStoreValue(a, b)					compareJsonbValue((a), (b))
 
+#define findUncompressedHStoreValueByValue(buffer, flags, lowbound, key)	\
+	findUncompressedJsonbValueByValue((buffer), (flags), (lowbound), (key))
+#define findUncompressedHStoreValue(buffer, flags, lowbound, key, keylen)	\
+	findUncompressedJsonbValue((buffer), (flags), (lowbound), (key), (keylen))
 
-/*
- * 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;
+#define getHStoreValue(buffer, flags, i)			getJsonbValue((buffer), (flags), (i))
+
+typedef ToJsonbState ToHStoreState;
+#define pushHStoreValue(state, r /* WHS_* */, v)	pushJsonbValue((state), (r), (v))
+
+extern bool stringIsNumber(char *string, int len, bool jsonNumber);
+
+extern uint32 compressHStore(HStoreValue *v, char *buffer);
+
+typedef JsonbIterator HStoreIterator;
+
+#define	HStoreIteratorInit(buffer)					JsonbIteratorInit(buffer)
 
-extern int	hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen);
-extern HStore *hstorePairs(Pairs *pairs, int32 pcount, int32 buflen);
+#define HStoreIteratorGet(it, v, skipNested)	JsonbIteratorGet((it), (v), (skipNested))
 
-extern size_t hstoreCheckKeyLen(size_t len);
-extern size_t hstoreCheckValLen(size_t len);
+text* HStoreValueToText(HStoreValue *v);
 
-extern int	hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen);
-extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs);
+extern HStoreValue* parseHStore(const char *str, int len, bool json);
+
+#define uniqueHStoreValue(v) uniqueJsonbValue(v)
 
 #define HStoreContainsStrategyNumber	7
 #define HStoreExistsStrategyNumber		9
@@ -194,4 +162,18 @@ extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs);
 	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__ */
diff --git a/contrib/hstore/hstore_compat.c b/contrib/hstore/hstore_compat.c
index 6327a8e..0e18505 100644
--- a/contrib/hstore/hstore_compat.c
+++ b/contrib/hstore/hstore_compat.c
@@ -105,9 +105,41 @@ typedef struct
 				pos:31;
 } HOldEntry;
 
-static int	hstoreValidNewFormat(HStore *hs);
-static int	hstoreValidOldFormat(HStore *hs);
+/*
+ * 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))
 
+#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_)]))
+
+/* 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.
@@ -116,7 +148,7 @@ static int	hstoreValidOldFormat(HStore *hs);
  *	2 = exactly valid
  */
 static int
-hstoreValidNewFormat(HStore *hs)
+hstoreValidNewFormat(HStoreV2 *hs)
 {
 	int			count = HS_COUNT(hs);
 	HEntry	   *entries = ARRPTR(hs);
@@ -168,7 +200,7 @@ hstoreValidNewFormat(HStore *hs)
  *	2 = exactly valid
  */
 static int
-hstoreValidOldFormat(HStore *hs)
+hstoreValidOldFormat(HStoreV2 *hs)
 {
 	int			count = hs->size_;
 	HOldEntry  *entries = (HOldEntry *) ARRPTR(hs);
@@ -235,16 +267,26 @@ hstoreValidOldFormat(HStore *hs)
 HStore *
 hstoreUpgrade(Datum orig)
 {
-	HStore	   *hs = (HStore *) PG_DETOAST_DATUM(orig);
+	HStoreV2	   *hs = (HStoreV2 *) PG_DETOAST_DATUM(orig);
 	int			valid_new;
 	int			valid_old;
 	bool		writable;
 
 	/* Return immediately if no conversion needed */
-	if ((hs->size_ & HS_FLAG_NEWVERSION) ||
+	if (VARSIZE_ANY(hs) <= VARHDRSZ ||
+		(hs->size_ & HS_FLAG_NEWVERSION) ||
 		hs->size_ == 0 ||
 		(VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0]))))
-		return hs;
+	{
+		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;
+	}
 
 	valid_new = hstoreValidNewFormat(hs);
 	valid_old = hstoreValidOldFormat(hs);
@@ -266,7 +308,7 @@ hstoreUpgrade(Datum orig)
 				HS_SETCOUNT(hs, HS_COUNT(hs));
 				HS_FIXSIZE(hs, HS_COUNT(hs));
 			}
-			return hs;
+			return (HStore*)hs;
 		}
 		else
 		{
@@ -323,7 +365,7 @@ hstoreUpgrade(Datum orig)
 	 */
 
 	if (!writable)
-		hs = (HStore *) PG_DETOAST_DATUM_COPY(orig);
+		hs = (HStoreV2 *) PG_DETOAST_DATUM_COPY(orig);
 
 	{
 		int			count = hs->size_;
@@ -352,7 +394,7 @@ hstoreUpgrade(Datum orig)
 		HS_FIXSIZE(hs, count);
 	}
 
-	return hs;
+	return (HStore*)hs;
 }
 
 
@@ -361,7 +403,7 @@ Datum		hstore_version_diag(PG_FUNCTION_ARGS);
 Datum
 hstore_version_diag(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = (HStore *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+	HStoreV2	   *hs = (HStoreV2 *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
 	int			valid_new = hstoreValidNewFormat(hs);
 	int			valid_old = hstoreValidOldFormat(hs);
 
diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c
index 2007801..ac77e2e 100644
--- a/contrib/hstore/hstore_gin.c
+++ b/contrib/hstore/hstore_gin.c
@@ -6,21 +6,11 @@
 #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);
 
@@ -41,37 +31,82 @@ 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:
+			cstr = DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric)));
+			item = makeitem(cstr, strlen(cstr), flag);
+			break;
+		case hsvString:
+			item = makeitem(v->string.val, v->string.len, flag);
+			break;
+		default:
+			elog(ERROR, "Wrong hstore 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;
-	HEntry	   *hsent = ARRPTR(hs);
-	char	   *ptr = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			i;
+	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;
 
-	*nentries = 2 * count;
-	if (count)
-		entries = (Datum *) palloc(sizeof(Datum) * 2 * count);
-
-	for (i = 0; i < count; ++i)
+	if (total == 0)
 	{
-		text	   *item;
+		*nentries = 0;
+		PG_RETURN_POINTER(NULL);
+	}
 
-		item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i),
-						KEYFLAG);
-		entries[2 * i] = PointerGetDatum(item);
+	entries = (Datum *) palloc(sizeof(Datum) * total);
 
-		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);
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, false)) != 0)
+	{
+		if (i >= total)
+		{
+			total *= 2;
+			entries = (Datum *) repalloc(entries, sizeof(Datum) * total);
+		}
+
+		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;
+		}
 	}
 
+	*nentries = i;
+
 	PG_RETURN_POINTER(entries);
 }
 
@@ -129,7 +164,8 @@ 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);
 		}
 
@@ -211,3 +247,196 @@ 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:
+			COMP_CRC32(stack->hash_state,
+					   VARDATA_ANY(v->numeric), VARSIZE_ANY_EXHDR(v->numeric));
+			break;
+		case hsvString:
+			COMP_CRC32(stack->hash_state, v->string.val, v->string.len);
+			break;
+		default:
+			elog(ERROR, "Shouldn't take hash of array");
+			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);
+}
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index 9001180..05b3f0a 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -6,8 +6,8 @@
 #include "access/gist.h"
 #include "access/skey.h"
 #include "catalog/pg_type.h"
+#include "utils/pg_crc.h"
 
-#include "crc32.h"
 #include "hstore.h"
 
 /* bigint defines */
@@ -105,6 +105,66 @@ 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:
+			COMP_CRC32(crc, VARDATA_ANY(v->numeric), VARSIZE_ANY_EXHDR(v->numeric));
+			break;
+		default:
+			elog(PANIC, "impossible value %d", v->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)
 {
@@ -113,25 +173,26 @@ ghstore_compress(PG_FUNCTION_ARGS)
 
 	if (entry->leafkey)
 	{
-		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;
+		GISTTYPE   		*res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+		HStore	   		*val = DatumGetHStoreP(entry->key);
 
 		SET_VARSIZE(res, CALCGTSIZE(0));
 
-		for (i = 0; i < count; ++i)
+		if (!HS_ISEMPTY(val))
 		{
-			int			h;
+			int				r;
+			HStoreIterator	*it = HStoreIteratorInit(VARDATA(val));
+			HStoreValue		v;
 
-			h = crc32_sz((char *) HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i));
-			HASH(GETSIGN(res), h);
-			if (!HS_VALISNULL(hsent, i))
+			while((r = HStoreIteratorGet(&it, &v, false)) != 0)
 			{
-				h = crc32_sz((char *) HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i));
-				HASH(GETSIGN(res), h);
+				if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) &&
+					v.type != hsvNull)
+				{
+					int   h = crc32_HStoreValue(&v, r);
+
+					HASH(GETSIGN(res), h);
+				}
 			}
 		}
 
@@ -396,7 +457,8 @@ 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)))
@@ -410,7 +472,8 @@ 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);
@@ -490,7 +553,6 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	PG_RETURN_POINTER(v);
 }
 
-
 Datum
 ghstore_consistent(PG_FUNCTION_ARGS)
 {
@@ -513,82 +575,123 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 	if (strategy == HStoreContainsStrategyNumber ||
 		strategy == HStoreOldContainsStrategyNumber)
 	{
-		HStore	   *query = PG_GETARG_HS(1);
-		HEntry	   *qe = ARRPTR(query);
-		char	   *qv = STRPTR(query);
-		int			count = HS_COUNT(query);
+		BITVECP		qe;
 		int			i;
 
-		for (i = 0; res && i < count; ++i)
+		qe = fcinfo->flinfo->fn_extra;
+		if (qe == NULL)
 		{
-			int			crc = crc32_sz((char *) HS_KEY(qe, qv, i), HS_KEYLEN(qe, i));
+			HStore	   		*query = PG_GETARG_HS(1);
+
+			qe = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(BITVEC));
+			memset(qe, 0, sizeof(BITVEC));
 
-			if (GETBIT(sign, HASHVAL(crc)))
+			if (!HS_ISEMPTY(query))
 			{
-				if (!HS_VALISNULL(qe, i))
+				int				r;
+				HStoreIterator	*it = HStoreIteratorInit(VARDATA(query));
+				HStoreValue		v;
+
+				while((r = HStoreIteratorGet(&it, &v, false)) != 0)
 				{
-					crc = crc32_sz((char *) HS_VAL(qe, qv, i), HS_VALLEN(qe, i));
-					if (!GETBIT(sign, HASHVAL(crc)))
-						res = false;
+					if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) && v.type != hsvNull)
+					{
+						int   crc = crc32_HStoreValue(&v, r);
+
+						HASH(qe, crc);
+					}
 				}
 			}
-			else
+
+			fcinfo->flinfo->fn_extra = qe;
+		}
+
+		LOOPBYTE
+		{
+			if ((sign[i] & qe[i]) != qe[i])
+			{
 				res = false;
+				break;
+			}
 		}
 	}
 	else if (strategy == HStoreExistsStrategyNumber)
 	{
-		text	   *query = PG_GETARG_TEXT_PP(1);
-		int			crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
+		int 	*qval;
 
-		res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
-	}
-	else if (strategy == HStoreExistsAllStrategyNumber)
-	{
-		ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
-		Datum	   *key_datums;
-		bool	   *key_nulls;
-		int			key_count;
-		int			i;
-
-		deconstruct_array(query,
-						  TEXTOID, -1, false, 'i',
-						  &key_datums, &key_nulls, &key_count);
-
-		for (i = 0; res && i < key_count; ++i)
+		qval = fcinfo->flinfo->fn_extra;
+		if (qval == NULL)
 		{
-			int			crc;
+			text	   *query = PG_GETARG_TEXT_PP(1);
+			int			crc = crc32_Key(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
 
-			if (key_nulls[i])
-				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (!(GETBIT(sign, HASHVAL(crc))))
-				res = FALSE;
+			qval = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(*qval));
+			*qval = HASHVAL(crc);
+
+			fcinfo->flinfo->fn_extra = qval;
 		}
+
+		res = (GETBIT(sign, *qval)) ? true : false;
 	}
-	else if (strategy == HStoreExistsAnyStrategyNumber)
+	else if (strategy == HStoreExistsAllStrategyNumber ||
+			 strategy == HStoreExistsAnyStrategyNumber)
 	{
-		ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
-		Datum	   *key_datums;
-		bool	   *key_nulls;
-		int			key_count;
-		int			i;
+		BITVECP	arrentry;
+		int		i;
 
-		deconstruct_array(query,
-						  TEXTOID, -1, false, 'i',
-						  &key_datums, &key_nulls, &key_count);
+		arrentry = fcinfo->flinfo->fn_extra;
+		if (arrentry == NULL)
+		{
+			ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
+			Datum	   *key_datums;
+			bool	   *key_nulls;
+			int			key_count;
 
-		res = FALSE;
+			arrentry = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+										  sizeof(BITVEC));
+			memset(arrentry, 0, sizeof(BITVEC));
 
-		for (i = 0; !res && i < key_count; ++i)
+			deconstruct_array(query,
+							  TEXTOID, -1, false, 'i',
+							  &key_datums, &key_nulls, &key_count);
+
+			for (i = 0; i < key_count; ++i)
+			{
+				int			crc;
+
+				if (key_nulls[i])
+					continue;
+				crc = crc32_Key(VARDATA(key_datums[i]),
+								VARSIZE(key_datums[i]) - VARHDRSZ);
+				HASH(arrentry, crc);
+			}
+
+			fcinfo->flinfo->fn_extra = arrentry;
+		}
+
+		if (strategy == HStoreExistsAllStrategyNumber)
 		{
-			int			crc;
+			LOOPBYTE
+			{
+				if ((sign[i] & arrentry[i]) != arrentry[i])
+				{
+					res = false;
+					break;
+				}
+			}
+		}
+		else /* HStoreExistsAnyStrategyNumber */
+		{
+			res = false;
 
-			if (key_nulls[i])
-				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (GETBIT(sign, HASHVAL(crc)))
-				res = TRUE;
+			LOOPBYTE
+			{
+				if (sign[i] & arrentry[i])
+				{
+					res = true;
+					break;
+				}
+			}
 		}
 	}
 	else
diff --git a/contrib/hstore/hstore_gram.y b/contrib/hstore/hstore_gram.y
new file mode 100644
index 0000000..d0883fa
--- /dev/null
+++ b/contrib/hstore/hstore_gram.y
@@ -0,0 +1,341 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 973a126..3bfbe56 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -7,12 +7,16 @@
 
 #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/syscache.h"
 #include "utils/typcache.h"
 
 #include "hstore.h"
@@ -22,507 +26,454 @@ 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))
 
-typedef struct
+static void recvHStore(StringInfo buf, HStoreValue *v, uint32 level,
+					   uint32 header);
+static Oid searchCast(Oid src, Oid dst, CoercionMethod *method);
+
+typedef enum HStoreOutputKind {
+	JsonOutput = 0x01,
+	LooseOutput = 0x02,
+	ArrayCurlyBraces = 0x04,
+	RootHashDecorated = 0x08,
+	PrettyPrint = 0x10
+} HStoreOutputKind;
+
+static char* HStoreToCString(StringInfo out, char *in,
+							 int len /* just estimation */, HStoreOutputKind kind);
+
+static size_t
+hstoreCheckKeyLen(size_t len)
 {
-	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
+	if (len > HSTORE_MAX_KEY_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for hstore key")));
+	return len;
+}
 
-static bool
-get_val(HSParser *state, bool ignoreeq, bool *escaped)
+static size_t
+hstoreCheckValLen(size_t len)
 {
-	int			st = GV_WAITVAL;
+	if (len > HSTORE_MAX_VALUE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for hstore value")));
+	return len;
+}
 
-	state->wordlen = 32;
-	state->cur = state->word = palloc(state->wordlen);
-	*escaped = false;
 
-	while (1)
+static HStore*
+hstoreDump(HStoreValue *p)
+{
+	uint32			buflen;
+	HStore	 	   *out;
+
+	if (p == NULL)
 	{
-		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__);
+		buflen = 0;
+		out = palloc(VARHDRSZ);
+	}
+	else
+	{
+		buflen = VARHDRSZ + p->size;
+		out = palloc(buflen);
+		SET_VARSIZE(out, buflen);
 
-		state->ptr++;
+		buflen = compressHStore(p, VARDATA(out));
 	}
+	SET_VARSIZE(out, buflen + VARHDRSZ);
+
+	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)));
 }
 
-#define WKEY	0
-#define WVAL	1
-#define WEQ 2
-#define WGT 3
-#define WDEL	4
+static void
+recvHStoreValue(StringInfo buf, HStoreValue *v, uint32 level, int c)
+{
+	uint32  hentry = c & HENTRY_TYPEMASK;
 
+	if (c == -1 /* compatibility */ || hentry == HENTRY_ISNULL)
+	{
+		v->type = hsvNull;
+		v->size = sizeof(HEntry);
+	}
+	else if (hentry == HENTRY_ISHASH || hentry == HENTRY_ISARRAY ||
+			 hentry == HENTRY_ISCALAR)
+	{
+		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, &c);
+		v->string.len = hstoreCheckKeyLen(c);
+		v->size = sizeof(HEntry) + v->string.len;
+	}
+	else
+	{
+		elog(ERROR, "bogus input");
+	}
+}
 
 static void
-parse_hstore(HSParser *state)
+recvHStore(StringInfo buf, HStoreValue *v, uint32 level, uint32 header)
 {
-	int			st = WKEY;
-	bool		escaped = false;
+	uint32	hentry;
+	uint32	i;
 
-	state->plen = 16;
-	state->pairs = (Pairs *) palloc(sizeof(Pairs) * state->plen);
-	state->pcur = 0;
-	state->ptr = state->begin;
-	state->word = NULL;
+	hentry = header & HENTRY_TYPEMASK;
 
-	while (1)
+	if (level == 0 && hentry == 0)
+		hentry = HENTRY_ISHASH; /* old version */
+
+	v->size = 3 * sizeof(HEntry);
+	if (hentry == HENTRY_ISHASH)
 	{
-		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)
+		v->type = hsvHash;
+		v->hash.npairs = header & HS_COUNT_MASK;
+		if (v->hash.npairs > 0)
 		{
-			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)
+			v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+			for(i=0; i<v->hash.npairs; i++)
 			{
-				state->word[4] = '\0';
-				if (0 == pg_strcasecmp(state->word, "null"))
-					state->pairs[state->pcur].isnull = true;
+				recvHStoreValue(buf, &v->hash.pairs[i].key, level,
+								pq_getmsgint(buf, 4));
+				if (v->hash.pairs[i].key.type != hsvString)
+					elog(ERROR, "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;
 			}
-			state->word = NULL;
-			state->pcur++;
-			st = WDEL;
+
+			uniqueHStoreValue(v);
 		}
-		else if (st == WDEL)
+	}
+	else if (hentry == HENTRY_ISARRAY || hentry == HENTRY_ISCALAR)
+	{
+		v->type = hsvArray;
+		v->array.nelems = header & HS_COUNT_MASK;
+		v->array.scalar = (hentry == HENTRY_ISCALAR) ? true : false;
+
+		if (v->array.scalar && v->array.nelems != 1)
+			elog(ERROR, "bogus input");
+
+		if (v->array.nelems > 0)
 		{
-			if (*(state->ptr) == ',')
-			{
-				st = WKEY;
-			}
-			else if (*(state->ptr) == '\0')
-			{
-				return;
-			}
-			else if (!isspace((unsigned char) *(state->ptr)))
+			v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+			for(i=0; i<v->array.nelems; i++)
 			{
-				elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
+				recvHStoreValue(buf, v->array.elems + i, level,
+								pq_getmsgint(buf, 4));
+				v->size += v->array.elems[i].size;
 			}
 		}
-		else
-			elog(ERROR, "Unknown state %d at line %d in file '%s'", st, __LINE__, __FILE__);
-
-		state->ptr++;
+	}
+	else
+	{
+			elog(ERROR, "bogus input");
 	}
 }
 
-static int
-comparePairs(const void *a, const void *b)
+PG_FUNCTION_INFO_V1(hstore_recv);
+Datum		hstore_recv(PG_FUNCTION_ARGS);
+Datum
+hstore_recv(PG_FUNCTION_ARGS)
 {
-	const Pairs *pa = a;
-	const Pairs *pb = b;
-
-	if (pa->keylen == pb->keylen)
-	{
-		int			res = memcmp(pa->key, pb->key, pa->keylen);
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	HStoreValue	v;
 
-		if (res)
-			return res;
+	recvHStore(buf, &v, 0, pq_getmsgint(buf, 4));
 
-		/* 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;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-/*
- * 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)
+PG_FUNCTION_INFO_V1(hstore_from_text);
+Datum		hstore_from_text(PG_FUNCTION_ARGS);
+Datum
+hstore_from_text(PG_FUNCTION_ARGS)
 {
-	Pairs	   *ptr,
-			   *res;
+	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);
 
-	*buflen = 0;
-	if (l < 2)
+	if (PG_ARGISNULL(1))
 	{
-		if (l == 1)
-			*buflen = a->keylen + ((a->isnull) ? 0 : a->vallen);
-		return l;
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
 	}
-
-	qsort((void *) a, l, sizeof(Pairs), comparePairs);
-	ptr = a + 1;
-	res = a;
-	while (ptr - a < l)
+	else
 	{
-		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));
-		}
+		text	   	*val = NULL;
 
-		ptr++;
+		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);
 	}
 
-	*buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
-	return res + 1 - a;
-}
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-size_t
-hstoreCheckKeyLen(size_t len)
-{
-	if (len > HSTORE_MAX_KEY_LEN)
-		ereport(ERROR,
-				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
-				 errmsg("string too long for hstore key")));
-	return len;
-}
-
-size_t
-hstoreCheckValLen(size_t len)
-{
-	if (len > HSTORE_MAX_VALUE_LEN)
-		ereport(ERROR,
-				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
-				 errmsg("string too long for hstore value")));
-	return len;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-HStore *
-hstorePairs(Pairs *pairs, int32 pcount, int32 buflen)
+PG_FUNCTION_INFO_V1(hstore_from_bool);
+Datum		hstore_from_bool(PG_FUNCTION_ARGS);
+Datum
+hstore_from_bool(PG_FUNCTION_ARGS)
 {
-	HStore	   *out;
-	HEntry	   *entry;
-	char	   *ptr;
-	char	   *buf;
-	int32		len;
-	int32		i;
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	len = CALCDATASIZE(pcount, buflen);
-	out = palloc(len);
-	SET_VARSIZE(out, len);
-	HS_SETCOUNT(out, pcount);
-
-	if (pcount == 0)
-		return out;
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	entry = ARRPTR(out);
-	buf = ptr = STRPTR(out);
+	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);
 
-	for (i = 0; i < pcount; i++)
-		HS_ADDITEM(entry, buf, ptr, pairs[i]);
+	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);
+	}
 
-	HS_FINALIZE(out, pcount, buf, ptr);
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-	return out;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_in);
-Datum		hstore_in(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_from_numeric);
+Datum		hstore_from_numeric(PG_FUNCTION_ARGS);
 Datum
-hstore_in(PG_FUNCTION_ARGS)
+hstore_from_numeric(PG_FUNCTION_ARGS)
 {
-	HSParser	state;
-	int32		buflen;
-	HStore	   *out;
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	state.begin = PG_GETARG_CSTRING(0);
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	parse_hstore(&state);
+	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);
 
-	state.pcur = hstoreUniquePairs(state.pairs, state.pcur, &buflen);
+	if (PG_ARGISNULL(1))
+	{
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
+	}
+	else
+	{
+		pair.value.type = hsvNumeric;
+		pair.value.numeric = PG_GETARG_NUMERIC(1);
+		pair.value.size = sizeof(HEntry) + sizeof(HEntry) +
+							VARSIZE_ANY(pair.value.numeric);
+	}
 
-	out = hstorePairs(state.pairs, state.pcur, buflen);
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_recv);
-Datum		hstore_recv(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_from_th);
+Datum		hstore_from_th(PG_FUNCTION_ARGS);
 Datum
-hstore_recv(PG_FUNCTION_ARGS)
+hstore_from_th(PG_FUNCTION_ARGS)
 {
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
-	int32		i;
-	int32		pcount;
-	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	pcount = pq_getmsgint(buf, 4);
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	if (pcount == 0)
+	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))
 	{
-		out = hstorePairs(NULL, 0, 0);
-		PG_RETURN_POINTER(out);
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
 	}
+	else
+	{
+		HStore	   	*val = NULL;
 
-	pairs = palloc(pcount * sizeof(Pairs));
+		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;
+	}
 
-	for (i = 0; i < pcount; ++i)
-	{
-		int			rawlen = pq_getmsgint(buf, 4);
-		int			len;
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-		if (rawlen < 0)
-			ereport(ERROR,
-					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-					 errmsg("null value not allowed for hstore key")));
+	PG_RETURN_POINTER(hstoreDump(&v));
+}
 
-		pairs[i].key = pq_getmsgtext(buf, rawlen, &len);
-		pairs[i].keylen = hstoreCheckKeyLen(len);
-		pairs[i].needfree = true;
+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;
 
-		rawlen = pq_getmsgint(buf, 4);
-		if (rawlen < 0)
-		{
-			pairs[i].val = NULL;
-			pairs[i].vallen = 0;
-			pairs[i].isnull = true;
-		}
-		else
-		{
-			pairs[i].val = pq_getmsgtext(buf, rawlen, &len);
-			pairs[i].vallen = hstoreCheckValLen(len);
-			pairs[i].isnull = false;
-		}
+	if (PG_ARGISNULL(0))
+	{
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
 	}
+	else
+	{
+		text	*scalar;
 
-	pcount = hstoreUniquePairs(pairs, pcount, &buflen);
+		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);
+	}
 
-	out = hstorePairs(pairs, pcount, buflen);
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&a));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_from_text);
-Datum		hstore_from_text(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_scalar_from_bool);
+Datum		hstore_scalar_from_bool(PG_FUNCTION_ARGS);
 Datum
-hstore_from_text(PG_FUNCTION_ARGS)
+hstore_scalar_from_bool(PG_FUNCTION_ARGS)
 {
-	text	   *key;
-	text	   *val = NULL;
-	Pairs		p;
-	HStore	   *out;
+	HStoreValue	a, v;
 
 	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
+	{
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
+	}
+	else
+	{
+		v.type = hsvBool;
+		v.boolean = PG_GETARG_BOOL(0);
+		v.size = sizeof(HEntry);
+	}
 
-	p.needfree = false;
-	key = PG_GETARG_TEXT_PP(0);
-	p.key = VARDATA_ANY(key);
-	p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	if (PG_ARGISNULL(1))
+	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))
 	{
-		p.vallen = 0;
-		p.isnull = true;
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
 	}
 	else
 	{
-		val = PG_GETARG_TEXT_PP(1);
-		p.val = VARDATA_ANY(val);
-		p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
-		p.isnull = false;
+		v.type = hsvNumeric;
+		v.numeric = PG_GETARG_NUMERIC(0);
+		v.size = VARSIZE_ANY(v.numeric) + 2*sizeof(HEntry);
 	}
 
-	out = hstorePairs(&p, 1, p.keylen + p.vallen);
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&a));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_from_arrays);
 Datum		hstore_from_arrays(PG_FUNCTION_ARGS);
 Datum
 hstore_from_arrays(PG_FUNCTION_ARGS)
 {
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
+	HStoreValue v;
 	Datum	   *key_datums;
 	bool	   *key_nulls;
 	int			key_count;
@@ -589,7 +540,10 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
 		Assert(key_count == value_count);
 	}
 
-	pairs = palloc(key_count * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2 * sizeof(HEntry);
+	v.hash.pairs = palloc(key_count * sizeof(*v.hash.pairs));
+	v.hash.npairs = key_count;
 
 	for (i = 0; i < key_count; ++i)
 	{
@@ -598,31 +552,34 @@ 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])
 		{
-			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;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
 		else
 		{
-			pairs[i].key = VARDATA_ANY(key_datums[i]);
-			pairs[i].val = VARDATA_ANY(value_datums[i]);
-			pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
-			pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i]));
-			pairs[i].isnull = false;
-			pairs[i].needfree = false;
+			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;
 		}
+
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
 	}
 
-	key_count = hstoreUniquePairs(pairs, key_count, &buflen);
+	uniqueHStoreValue(&v);
 
-	out = hstorePairs(pairs, key_count, buflen);
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
 
@@ -634,9 +591,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
 	ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
 	int			ndims = ARR_NDIM(in_array);
 	int			count;
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
+	HStoreValue	v;
 	Datum	   *in_datums;
 	bool	   *in_nulls;
 	int			in_count;
@@ -647,8 +602,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
 	switch (ndims)
 	{
 		case 0:
-			out = hstorePairs(NULL, 0, 0);
-			PG_RETURN_POINTER(out);
+			PG_RETURN_POINTER(hstoreDump(NULL));
 
 		case 1:
 			if ((ARR_DIMS(in_array)[0]) % 2)
@@ -676,7 +630,10 @@ hstore_from_array(PG_FUNCTION_ARGS)
 
 	count = in_count / 2;
 
-	pairs = palloc(count * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2*sizeof(HEntry);
+	v.hash.npairs = count;
+	v.hash.pairs = palloc(count * sizeof(HStorePair));
 
 	for (i = 0; i < count; ++i)
 	{
@@ -685,31 +642,33 @@ 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])
 		{
-			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;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
 		else
 		{
-			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.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;
 		}
-	}
 
-	count = hstoreUniquePairs(pairs, count, &buflen);
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
+	}
 
-	out = hstorePairs(pairs, count, buflen);
+	uniqueHStoreValue(&v);
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
 /* most of hstore_from_record is shamelessly swiped from record_out */
@@ -739,19 +698,17 @@ Datum
 hstore_from_record(PG_FUNCTION_ARGS)
 {
 	HeapTupleHeader rec;
-	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;
+	HStore		   *out;
+	HStoreValue	   v;
+	Oid				tupType;
+	int32			tupTypmod;
+	TupleDesc		tupdesc;
+	HeapTupleData 	tuple;
+	RecordIOData   *my_extra;
+	int				ncolumns;
+	int				i;
+	Datum	   	   *values;
+	bool	   	   *nulls;
 
 	if (PG_ARGISNULL(0))
 	{
@@ -807,7 +764,10 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	pairs = palloc(ncolumns * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2*sizeof(HEntry);
+	v.hash.npairs = ncolumns;
+	v.hash.pairs = palloc(ncolumns * sizeof(HStorePair));
 
 	if (rec)
 	{
@@ -829,7 +789,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		nulls = NULL;
 	}
 
-	for (i = 0, j = 0; i < ncolumns; ++i)
+	for (i = 0; i < ncolumns; ++i)
 	{
 		ColumnIOData *column_info = &my_extra->columns[i];
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
@@ -839,46 +799,82 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		if (tupdesc->attrs[i]->attisdropped)
 			continue;
 
-		pairs[j].key = NameStr(tupdesc->attrs[i]->attname);
-		pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(tupdesc->attrs[i]->attname)));
+		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;
 
 		if (!nulls || nulls[i])
 		{
-			pairs[j].val = NULL;
-			pairs[j].vallen = 4;
-			pairs[j].isnull = true;
-			pairs[j].needfree = false;
-			++j;
-			continue;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
-
-		/*
-		 * Convert the column value to text
-		 */
-		if (column_info->column_type != column_type)
+		else
 		{
-			bool		typIsVarlena;
+			/*
+			 * 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;
 
-			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.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]);
+				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;
+				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;
+			}
+		}
+
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
 	}
 
-	ncolumns = hstoreUniquePairs(pairs, j, &buflen);
+	uniqueHStoreValue(&v);
 
-	out = hstorePairs(pairs, ncolumns, buflen);
+	out = hstoreDump(&v);
 
 	ReleaseTupleDesc(tupdesc);
 
@@ -893,8 +889,6 @@ 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;
@@ -940,8 +934,6 @@ 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
@@ -949,7 +941,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 	 * domain nulls.
 	 */
 
-	if (HS_COUNT(hs) == 0 && rec)
+	if (HS_ISEMPTY(hs) && rec)
 		PG_RETURN_POINTER(rec);
 
 	tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
@@ -1009,94 +1001,464 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 		}
 	}
 
-	for (i = 0; i < ncolumns; ++i)
+	for (i = 0; i < ncolumns; ++i)
+	{
+		ColumnIOData *column_info = &my_extra->columns[i];
+		Oid			column_type = tupdesc->attrs[i]->atttypid;
+		HStoreValue	*v = NULL;
+
+		/* Ignore dropped columns in datatype */
+		if (tupdesc->attrs[i]->attisdropped)
+		{
+			nulls[i] = true;
+			continue;
+		}
+
+		if (!HS_ISEMPTY(hs))
+		{
+			char *key = NameStr(tupdesc->attrs[i]->attname);
+
+			v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HASH, NULL, key, strlen(key));
+		}
+
+		/*
+		 * we can't just skip here if the key wasn't found since we might have
+		 * a domain to deal with. If we were passed in a non-null record
+		 * datum, we assume that the existing values are valid (if they're
+		 * not, then it's not our fault), but if we were passed in a null,
+		 * 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)
+			continue;
+
+		/*
+		 * Prepare to convert the column value from text
+		 */
+		if (column_info->column_type != column_type)
+		{
+			getTypeInputInfo(column_type,
+							 &column_info->typiofunc,
+							 &column_info->typioparam);
+			fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
+						  fcinfo->flinfo->fn_mcxt);
+			column_info->column_type = column_type;
+		}
+
+		if (v == NULL || v->type == hsvNull)
+		{
+			/*
+			 * need InputFunctionCall to happen even for nulls, so that domain
+			 * checks are done
+			 */
+			values[i] = InputFunctionCall(&column_info->proc, NULL,
+										  column_info->typioparam,
+										  tupdesc->attrs[i]->atttypmod);
+			nulls[i] = true;
+		}
+		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(PANIC, "Wrong hstore");
+
+			values[i] = InputFunctionCall(&column_info->proc, s,
+										  column_info->typioparam,
+										  tupdesc->attrs[i]->atttypmod);
+			nulls[i] = false;
+		}
+	}
+
+	rettuple = heap_form_tuple(tupdesc, values, nulls);
+
+	ReleaseTupleDesc(tupdesc);
+
+	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)
+{
+	if (kind & PrettyPrint)
+	{
+		int i;
+
+		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)
+	{
+		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);
+	}
+}
+
+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(PANIC, "Unknown type");
+	}
+}
+
+static bool
+needBrackets(int level, bool isArray, HStoreOutputKind kind, bool isScalar)
+{
+	bool res;
+
+	if (isArray && isScalar)
+		res = false;
+	else if (level == 0)
+		res = (isArray || (kind & RootHashDecorated)) ? true : false;
+	else
+		res = true;
+
+	return res;
+}
+
+static bool
+isArrayBrackets(HStoreOutputKind kind)
+{
+	return ((kind & ArrayCurlyBraces) == 0) ? true : false;
+}
+		
+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;
+
+	if (out == NULL)
+		out = makeStringInfo();
+
+	if (in == NULL)
 	{
-		ColumnIOData *column_info = &my_extra->columns[i];
-		Oid			column_type = tupdesc->attrs[i]->atttypid;
-		char	   *value;
-		int			idx;
-		int			vallen;
+		appendStringInfoString(out, "");
+		return out->data;
+	}
 
-		/* Ignore dropped columns in datatype */
-		if (tupdesc->attrs[i]->attisdropped)
+	enlargeStringInfo(out, (len >= 0) ? len : 64);
+
+	it = HStoreIteratorInit(in);
+
+	while((type = HStoreIteratorGet(&it, &v, false)) != 0)
+	{
+reout:
+		switch(type)
 		{
-			nulls[i] = true;
-			continue;
-		}
+			case WHS_BEGIN_ARRAY:
+				if (first == false)
+				{
+					appendBinaryStringInfo(out, ", ", 2);
+					printCR(out, kind);
+				}
+				first = true;
 
-		idx = hstoreFindKey(hs, 0,
-							NameStr(tupdesc->attrs[i]->attname),
-							strlen(NameStr(tupdesc->attrs[i]->attname)));
+				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;
 
-		/*
-		 * we can't just skip here if the key wasn't found since we might have
-		 * a domain to deal with. If we were passed in a non-null record
-		 * datum, we assume that the existing values are valid (if they're
-		 * not, then it's not our fault), but if we were passed in a null,
-		 * 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 (idx < 0 && rec)
-			continue;
+				if (level == 0)
+					isRootHash = true;
 
-		/*
-		 * Prepare to convert the column value from text
-		 */
-		if (column_info->column_type != column_type)
-		{
-			getTypeInputInfo(column_type,
-							 &column_info->typiofunc,
-							 &column_info->typioparam);
-			fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
-						  fcinfo->flinfo->fn_mcxt);
-			column_info->column_type = column_type;
-		}
+				if (needBrackets(level, false, kind, false))
+				{
+					printIndent(out, isRootHash, kind, level);
+					appendStringInfoCharMacro(out, '{');
+					printCR(out, kind);
+				}
 
-		if (idx < 0 || HS_VALISNULL(entries, idx))
-		{
-			/*
-			 * need InputFunctionCall to happen even for nulls, so that domain
-			 * checks are done
-			 */
-			values[i] = InputFunctionCall(&column_info->proc, NULL,
-										  column_info->typioparam,
-										  tupdesc->attrs[i]->atttypmod);
-			nulls[i] = true;
-		}
-		else
-		{
-			vallen = HS_VALLEN(entries, idx);
-			value = palloc(1 + vallen);
-			memcpy(value, HS_VAL(entries, ptr, idx), vallen);
-			value[vallen] = 0;
+				level++;
+				break;
+			case WHS_KEY:
+				if (first == false)
+				{
+					appendBinaryStringInfo(out, ", ", 2);
+					printCR(out, kind);
+				}
+				first = true;
 
-			values[i] = InputFunctionCall(&column_info->proc, value,
-										  column_info->typioparam,
-										  tupdesc->attrs[i]->atttypmod);
-			nulls[i] = false;
+				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(PANIC, "Wrong flags");
 		}
 	}
 
-	rettuple = heap_form_tuple(tupdesc, values, nulls);
-
-	ReleaseTupleDesc(tupdesc);
+	Assert(level == 0);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(rettuple));
+	return out->data;
 }
 
-
-static char *
-cpw(char *dst, char *src, int len)
+text*
+HStoreValueToText(HStoreValue *v)
 {
-	char	   *ptr = src;
+	text		*out;
 
-	while (ptr - src < len)
+	if (v == NULL || v->type == hsvNull)
 	{
-		if (*ptr == '"' || *ptr == '\\')
-			*dst++ = '\\';
-		*dst++ = *ptr++;
+		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))
+		));
 	}
-	return dst;
+	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);
+	}
+
+	return out;
 }
 
 PG_FUNCTION_INFO_V1(hstore_out);
@@ -1104,105 +1466,85 @@ Datum		hstore_out(PG_FUNCTION_ARGS);
 Datum
 hstore_out(PG_FUNCTION_ARGS)
 {
-	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);
-
-	if (count == 0)
-		PG_RETURN_CSTRING(pstrdup(""));
-
-	buflen = 0;
-
-	/*
-	 * this loop overestimates due to pessimistic assumptions about escaping,
-	 * so very large hstore values can't be output. this could be fixed, but
-	 * many other data types probably have the same issue. This replaced code
-	 * that used the original varlena size for calculations, which was wrong
-	 * in some subtle ways.
-	 */
-
-	for (i = 0; i < count; i++)
-	{
-		/* include "" and => and comma-space */
-		buflen += 6 + 2 * HS_KEYLEN(entries, i);
-		/* include "" only if nonnull */
-		buflen += 2 + (HS_VALISNULL(entries, i)
-					   ? 2
-					   : 2 * HS_VALLEN(entries, i));
-	}
-
-	out = ptr = palloc(buflen);
-
-	for (i = 0; i < count; i++)
-	{
-		*ptr++ = '"';
-		ptr = cpw(ptr, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		*ptr++ = '"';
-		*ptr++ = '=';
-		*ptr++ = '>';
-		if (HS_VALISNULL(entries, i))
-		{
-			*ptr++ = 'N';
-			*ptr++ = 'U';
-			*ptr++ = 'L';
-			*ptr++ = 'L';
-		}
-		else
-		{
-			*ptr++ = '"';
-			ptr = cpw(ptr, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
-			*ptr++ = '"';
-		}
+	HStore	*hs = PG_GETARG_HS(0);
+	char 	*out;
 
-		if (i + 1 != count)
-		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
-		}
-	}
-	*ptr = '\0';
+	out = HStoreToCString(NULL, (HS_ISEMPTY(hs)) ? NULL : VARDATA(hs), 
+						  VARSIZE(hs), SET_PRETTY_PRINT_VAR(0));
 
 	PG_RETURN_CSTRING(out);
 }
 
-
 PG_FUNCTION_INFO_V1(hstore_send);
 Datum		hstore_send(PG_FUNCTION_ARGS);
 Datum
 hstore_send(PG_FUNCTION_ARGS)
 {
-	HStore	   *in = PG_GETARG_HS(0);
-	int			i;
-	int			count = HS_COUNT(in);
-	char	   *base = STRPTR(in);
-	HEntry	   *entries = ARRPTR(in);
-	StringInfoData buf;
+	HStore	   		*in = PG_GETARG_HS(0);
+	StringInfoData	buf;
 
 	pq_begintypsend(&buf);
 
-	pq_sendint(&buf, count, 4);
-
-	for (i = 0; i < count; i++)
+	if (HS_ISEMPTY(in))
 	{
-		int32		keylen = HS_KEYLEN(entries, i);
+		pq_sendint(&buf, 0, 4);
+	}
+	else
+	{
+		HStoreIterator	*it;
+		int				type;
+		HStoreValue		v;
+		uint32			flag;
+		bytea			*nbuf;
 
-		pq_sendint(&buf, keylen, 4);
-		pq_sendtext(&buf, HS_KEY(entries, base, i), keylen);
-		if (HS_VALISNULL(entries, i))
-		{
-			pq_sendint(&buf, -1, 4);
-		}
-		else
-		{
-			int32		vallen = HS_VALLEN(entries, i);
+		enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */);
+
+		it = HStoreIteratorInit(VARDATA_ANY(in));
 
-			pq_sendint(&buf, vallen, 4);
-			pq_sendtext(&buf, HS_VAL(entries, base, i), vallen);
+		while((type = HStoreIteratorGet(&it, &v, false)) != 0)
+		{
+			switch(type)
+			{
+				case WHS_BEGIN_ARRAY:
+					flag = (v.array.scalar) ? HENTRY_ISCALAR : 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, VARSIZE_ANY(nbuf) | HENTRY_ISNUMERIC, 4);
+							pq_sendbytes(&buf, (char*)nbuf, VARSIZE_ANY(nbuf));
+							break;
+						default:
+							elog(PANIC, "Wrong type: %u", v.type);
+					}
+					break;
+				case WHS_END_ARRAY:
+				case WHS_END_HASH:
+					break;
+				default:
+					elog(PANIC, "Wrong flags");
+			}
 		}
 	}
 
@@ -1224,124 +1566,28 @@ Datum
 hstore_to_json_loose(PG_FUNCTION_ARGS)
 {
 	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);
-	bool		is_number;
-	StringInfo	src,
-				dst;
-
-	if (count == 0)
-		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
-
-	buflen = 3;
+	text	   *out;
 
-	/*
-	 * Formula adjusted slightly from the logic in hstore_out. We have to take
-	 * account of out treatment of booleans to be a bit more pessimistic about
-	 * the length of values.
-	 */
+	if (HS_ISEMPTY(in))
+	{
+		out = cstring_to_text_with_len("{}",2);
+	}
+	else
+	{
+		StringInfo	str;
 
-	for (i = 0; i < count; i++)
-	{
-		/* include "" and colon-space and comma-space */
-		buflen += 6 + 2 * HS_KEYLEN(entries, i);
-		/* include "" only if nonnull */
-		buflen += 3 + (HS_VALISNULL(entries, i)
-					   ? 1
-					   : 2 * HS_VALLEN(entries, i));
-	}
-
-	out = ptr = palloc(buflen);
-
-	src = makeStringInfo();
-	dst = makeStringInfo();
-
-	*ptr++ = '{';
-
-	for (i = 0; i < count; i++)
-	{
-		resetStringInfo(src);
-		resetStringInfo(dst);
-		appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		escape_json(dst, src->data);
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
-		*ptr++ = ':';
-		*ptr++ = ' ';
-		resetStringInfo(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(src);
-			appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
+		str = makeStringInfo();
+		appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
 
-			/*
-			 * don't treat something with a leading zero followed by another
-			 * digit as numeric - could be a zip code or similar
-			 */
-			if (src->len > 0 &&
-				!(src->data[0] == '0' &&
-				  isdigit((unsigned char) src->data[1])) &&
-				strspn(src->data, "+-0123456789Ee.") == src->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(src->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;
+		HStoreToCString(str, VARDATA_ANY(in), VARSIZE_ANY(in), 
+						SET_PRETTY_PRINT_VAR(JsonOutput | RootHashDecorated | LooseOutput));
 
-					dval = strtod(src->data, &endptr);
-					(void) dval;
-					if (*endptr == '\0')
-						is_number = true;
-				}
-			}
-			if (is_number)
-				appendBinaryStringInfo(dst, src->data, src->len);
-			else
-				escape_json(dst, src->data);
-		}
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
+		out = (text*)str->data;
 
-		if (i + 1 != count)
-		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
-		}
+		SET_VARSIZE(out, str->len);
 	}
-	*ptr++ = '}';
-	*ptr = '\0';
 
-	PG_RETURN_TEXT_P(cstring_to_text(out));
+	PG_RETURN_TEXT_P(out);
 }
 
 PG_FUNCTION_INFO_V1(hstore_to_json);
@@ -1350,74 +1596,330 @@ Datum
 hstore_to_json(PG_FUNCTION_ARGS)
 {
 	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);
-	StringInfo	src,
-				dst;
+	text	   *out;
 
-	if (count == 0)
-		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
+	if (HS_ISEMPTY(in))
+	{
+		out = cstring_to_text_with_len("{}",2);
+	}
+	else
+	{
+		StringInfo	str;
 
-	buflen = 3;
+		str = makeStringInfo();
+		appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
 
-	/*
-	 * Formula adjusted slightly from the logic in hstore_out. We have to take
-	 * account of out treatment of booleans to be a bit more pessimistic about
-	 * the length of values.
-	 */
+		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;
+}
 
-	for (i = 0; i < count; i++)
+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;
+	Oid				castOid = InvalidOid;
+	int				valueType = hsvString;
+	FmgrInfo		castInfo;
+	CoercionMethod	method;
+
+	if (ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)) == 0)
+		PG_RETURN_POINTER(hstoreDump(NULL));
+
+	switch(ARR_ELEMTYPE(array))
 	{
-		/* include "" and colon-space and comma-space */
-		buflen += 6 + 2 * HS_KEYLEN(entries, i);
-		/* include "" only if nonnull */
-		buflen += 3 + (HS_VALISNULL(entries, i)
-					   ? 1
-					   : 2 * HS_VALLEN(entries, i));
+		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");
+
+				valueType = hsvString;
+				break;
+			}
 	}
 
-	out = ptr = palloc(buflen);
+	if (castOid != InvalidOid)
+		fmgr_info(castOid, &castInfo);
 
-	src = makeStringInfo();
-	dst = makeStringInfo();
+	iterator = array_create_iterator(array, 0);
 
-	*ptr++ = '{';
+	value.type = hsvArray;
+	value.array.scalar = false;
+	for(i=0; i<ncounters; i++)
+	{
+		value.array.nelems = dims[i];
+		result = pushHStoreValue(&state, WHS_BEGIN_ARRAY, &value);
+	}
 
-	for (i = 0; i < count; i++)
+	while(array_iterate(iterator, &datum, &isnull))
 	{
-		resetStringInfo(src);
-		resetStringInfo(dst);
-		appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		escape_json(dst, src->data);
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
-		*ptr++ = ':';
-		*ptr++ = ' ';
-		resetStringInfo(dst);
-		if (HS_VALISNULL(entries, i))
-			appendStringInfoString(dst, "null");
+		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);
+			}
+		}
 		else
 		{
-			resetStringInfo(src);
-			appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
-			escape_json(dst, src->data);
+			counters[i]++;
 		}
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
 
-		if (i + 1 != count)
+		if (isnull)
+		{
+			value.type = hsvNull;
+			value.size = sizeof(HEntry);
+		}
+		else
 		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
+			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, "Impossible state: %d", valueType);
+			}
 		}
+
+		result = pushHStoreValue(&state, WHS_ELEM, &value);
+	}
+
+	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);
+}
+
+PG_FUNCTION_INFO_V1(hstore2jsonb);
+Datum		hstore2jsonb(PG_FUNCTION_ARGS);
+Datum
+hstore2jsonb(PG_FUNCTION_ARGS)
+{
+	HStore	*hs = PG_GETARG_HS(0);
+	Jsonb	*jb = palloc(VARSIZE_ANY(hs));
+
+	memcpy(jb, hs, VARSIZE_ANY(hs));
+
+	if (VARSIZE_ANY_EXHDR(jb) >= sizeof(uint32))
+	{
+		uint32 *header = (uint32*)VARDATA_ANY(jb);
+
+		*header &= ~JB_FLAG_UNUSED;
+	}
+
+	PG_RETURN_JSONB(jb);
+}
+
+PG_FUNCTION_INFO_V1(jsonb2hstore);
+Datum		jsonb2hstore(PG_FUNCTION_ARGS);
+Datum
+jsonb2hstore(PG_FUNCTION_ARGS)
+{
+	Jsonb	*jb = PG_GETARG_JSONB(0);
+	HStore	*hs = palloc(VARSIZE_ANY(jb));
+
+	memcpy(hs, jb, VARSIZE_ANY(jb));
+
+	if (VARSIZE_ANY_EXHDR(hs) >= sizeof(uint32))
+	{
+		uint32	*header = (uint32*)VARDATA_ANY(hs);
+
+		*header |= HS_FLAG_NEWVERSION;
+	}
+
+	PG_RETURN_POINTER(hs);
+}
+
+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);
+
+	if (l > sizeof(uint32))
+	{
+		uint32	*header = (uint32*)buffer;
+
+		*header |= HS_FLAG_NEWVERSION;
 	}
-	*ptr++ = '}';
-	*ptr = '\0';
 
-	PG_RETURN_TEXT_P(cstring_to_text(out));
+	return l;
 }
+
+
+
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 45edb04..0af258c 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -25,154 +25,597 @@ 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;
 
-/*
- * 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)
+	deconstruct_array(a,
+					  TEXTOID, -1, false, 'i',
+					  &key_datums, &key_nulls, &key_count);
+
+	if (key_count == 0)
+		return NULL;
+
+	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)
 {
-	HEntry	   *entries = ARRPTR(hs);
-	int			stopLow = lowbound ? *lowbound : 0;
-	int			stopHigh = HS_COUNT(hs);
-	int			stopMiddle;
-	char	   *base = STRPTR(hs);
+	HStoreValue		*stopLow = a->array.elems + ((lowbound) ? *lowbound : 0),
+					*stopHigh = a->array.elems + a->array.nelems,
+					*stopMiddle;
 
 	while (stopLow < stopHigh)
 	{
-		int			difference;
+		int diff;
 
 		stopMiddle = stopLow + (stopHigh - stopLow) / 2;
 
-		if (HS_KEYLEN(entries, stopMiddle) == keylen)
-			difference = memcmp(HS_KEY(entries, base, stopMiddle), key, keylen);
+		if (keylen == stopMiddle->string.len)
+			diff = memcmp(stopMiddle->string.val, key, keylen);
 		else
-			difference = (HS_KEYLEN(entries, stopMiddle) > keylen) ? 1 : -1;
+			diff = (stopMiddle->string.len > keylen) ? 1 : -1;
 
-		if (difference == 0)
+		if (diff == 0)
 		{
 			if (lowbound)
-				*lowbound = stopMiddle + 1;
+				*lowbound = (stopMiddle - a->array.elems) + 1;
 			return stopMiddle;
 		}
-		else if (difference < 0)
+		else if (diff < 0)
+		{
 			stopLow = stopMiddle + 1;
+		}
 		else
+		{
 			stopHigh = stopMiddle;
+		}
 	}
 
 	if (lowbound)
-		*lowbound = stopLow;
-	return -1;
+		*lowbound = (stopLow - a->array.elems) + 1;
+
+	return NULL;
 }
 
-Pairs *
-hstoreArrayToPairs(ArrayType *a, int *npairs)
+PG_FUNCTION_INFO_V1(hstore_fetchval);
+Datum		hstore_fetchval(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval(PG_FUNCTION_ARGS)
 {
-	Datum	   *key_datums;
-	bool	   *key_nulls;
-	int			key_count;
-	Pairs	   *key_pairs;
-	int			bufsiz;
-	int			i,
-				j;
+	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);
+}
 
-	deconstruct_array(a,
-					  TEXTOID, -1, false, 'i',
-					  &key_datums, &key_nulls, &key_count);
+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));
 
-	if (key_count == 0)
+		memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+		PG_RETURN_NUMERIC(out);
+	}
+
+	PG_RETURN_NULL();
+}
+
+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)
 	{
-		*npairs = 0;
-		return NULL;
+		Numeric		out = palloc(VARSIZE_ANY(v->numeric));
+
+		memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+		PG_RETURN_NUMERIC(out);
 	}
 
-	key_pairs = palloc(sizeof(Pairs) * key_count);
+	PG_RETURN_NULL();
+}
 
-	for (i = 0, j = 0; i < key_count; i++)
+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)
+{
+	bool	negative = false;
+	char 	*p = c;
+
+	*acc = 0;
+
+	while(isspace(*p) && p - c < l)
+		p++;
+
+	if (p - c >= l)
+		return false;
+
+	if (*p == '-')
 	{
-		if (!key_nulls[i])
+		negative = true;
+		p++;
+	}
+	else if (*p == '+')
+	{
+		p++;
+	}
+
+	if (p - c >= l)
+		return false;
+
+
+	while(p - c < l)
+	{
+		if (!isdigit(*p))
+			return false;
+
+		*acc *= 10;
+		*acc += (*p - '0');
+		p++;
+	}
+
+	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)
+		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;
+
+	if (path_len == 0)
+		return v;
+
+	for(i=0; v != NULL && i<path_len; i++)
+	{
+		uint32	header;
+
+		if (v->type != hsvBinary || path_nulls[i])
+			return NULL;
+
+		header = *(uint32*)v->binary.data;
+
+		if (header & HS_FLAG_HASH)
 		{
-			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++;
+			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
+		{
+			elog(PANIC,"wrong header type: %08x", header);
 		}
 	}
 
-	*npairs = hstoreUniquePairs(key_pairs, j, &bufsiz);
+	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;
 
-	return key_pairs;
+	if ((out = HStoreValueToText(hstoreDeepFetch(hs, path))) == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_TEXT_P(out);
 }
 
+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);
 
-PG_FUNCTION_INFO_V1(hstore_fetchval);
-Datum		hstore_fetchval(PG_FUNCTION_ARGS);
+	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);
 Datum
-hstore_fetchval(PG_FUNCTION_ARGS)
+hstore_fetchval_path_boolean(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	HEntry	   *entries = ARRPTR(hs);
-	text	   *out;
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
+	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			*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);
 
-	if (idx < 0 || HS_VALISNULL(entries, idx))
+		Assert(v->type == hsvBinary);
+		SET_VARSIZE(out, VARHDRSZ + v->binary.len);
+		memcpy(VARDATA(out), v->binary.data, v->binary.len);
+	}
+
+	return out;
+}
+
+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)
 		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;
 
-	out = cstring_to_text_with_len(HS_VAL(entries, STRPTR(hs), idx),
-								   HS_VALLEN(entries, idx));
+	if (!HS_ISEMPTY(hs))
+		v = getHStoreValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, i);
 
-	PG_RETURN_TEXT_P(out);
+	if ((out = HStoreValueToHStore(v)) == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_POINTER(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);
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
+	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_RETURN_BOOL(idx >= 0);
+
+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);
+
+	PG_RETURN_BOOL(hstoreDeepFetch(hs, path) != NULL);
 }
 
 
+
 PG_FUNCTION_INFO_V1(hstore_exists_any);
 Datum		hstore_exists_any(PG_FUNCTION_ARGS);
 Datum
 hstore_exists_any(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(keys, &nkeys);
-	int			i;
-	int			lowbound = 0;
-	bool		res = false;
-
+	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;
 	/*
 	 * we exploit the fact that the pairs list is already sorted into strictly
-	 * increasing order to narrow the hstoreFindKey search; each search can
+	 * 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.
 	 */
-	for (i = 0; i < nkeys; i++)
+	for (i = 0; i < v->array.nelems; i++)
 	{
-		int			idx = hstoreFindKey(hs, &lowbound,
-									  key_pairs[i].key, key_pairs[i].keylen);
-
-		if (idx >= 0)
+		if (findUncompressedHStoreValueByValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, plowbound,
+											   v->array.elems + i) != NULL)
 		{
 			res = true;
 			break;
@@ -188,26 +631,36 @@ Datum		hstore_exists_all(PG_FUNCTION_ARGS);
 Datum
 hstore_exists_all(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(keys, &nkeys);
-	int			i;
-	int			lowbound = 0;
-	bool		res = true;
+	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);
+	}
 
+	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 hstoreFindKey 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 findUncompressedHStoreValue search;
+	 * each search can start one entry past the previous "found" entry,
+	 * or at the lower bound of the last search.
 	 */
-	for (i = 0; i < nkeys; i++)
+	for (i = 0; i < v->array.nelems; i++)
 	{
-		int			idx = hstoreFindKey(hs, &lowbound,
-									  key_pairs[i].key, key_pairs[i].keylen);
-
-		if (idx < 0)
+		if (findUncompressedHStoreValueByValue(VARDATA(hs),
+											   HS_FLAG_HASH | HS_FLAG_ARRAY,
+											   plowbound,
+											   v->array.elems + i) == NULL)
 		{
 			res = false;
 			break;
@@ -223,14 +676,18 @@ Datum		hstore_defined(PG_FUNCTION_ARGS);
 Datum
 hstore_defined(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	HEntry	   *entries = ARRPTR(hs);
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
-	bool		res = (idx >= 0 && !HS_VALISNULL(entries, idx));
-
-	PG_RETURN_BOOL(res);
+	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));
 }
 
 
@@ -239,483 +696,1344 @@ Datum		hstore_delete(PG_FUNCTION_ARGS);
 Datum
 hstore_delete(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	char	   *keyptr = VARDATA_ANY(key);
-	int			keylen = VARSIZE_ANY_EXHDR(key);
-	HStore	   *out = palloc(VARSIZE(hs));
-	char	   *bufs,
-			   *bufd,
-			   *ptrd;
-	HEntry	   *es,
-			   *ed;
-	int			i;
-	int			count = HS_COUNT(hs);
-	int			outcount = 0;
-
-	SET_VARSIZE(out, VARSIZE(hs));
-	HS_SETCOUNT(out, count);	/* temporary! */
+	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);
 
-	bufs = STRPTR(hs);
-	es = ARRPTR(hs);
-	bufd = ptrd = STRPTR(out);
-	ed = ARRPTR(out);
+	it = HStoreIteratorInit(VARDATA(in));
 
-	for (i = 0; i < count; ++i)
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		int			len = HS_KEYLEN(es, i);
-		char	   *ptrs = HS_KEY(es, bufs, i);
+		skipNested = true;
 
-		if (!(len == keylen && memcmp(ptrs, keyptr, keylen) == 0))
+		if ((r == WHS_ELEM || r == WHS_KEY) &&
+			(v.type == hsvString && keylen == v.string.len &&
+			 memcmp(keyptr, v.string.val, keylen) == 0))
 		{
-			int			vallen = HS_VALLEN(es, i);
+			if (r == WHS_KEY)
+				/* skip corresponding value */
+				HStoreIteratorGet(&it, &v, true);
 
-			HS_COPYITEM(ed, bufd, ptrd, ptrs, len, vallen, HS_VALISNULL(es, i));
-			++outcount;
+			continue;
 		}
+
+		res = pushHStoreValue(&toState, r, &v);
 	}
 
-	HS_FINALIZE(out, outcount, bufd, ptrd);
+	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);
+	}
 
 	PG_RETURN_POINTER(out);
 }
 
-
 PG_FUNCTION_INFO_V1(hstore_delete_array);
 Datum		hstore_delete_array(PG_FUNCTION_ARGS);
 Datum
 hstore_delete_array(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HStore	   *out = palloc(VARSIZE(hs));
-	int			hs_count = HS_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)
+	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)
 	{
-		/* return a copy of the input, unchanged */
-		memcpy(out, hs, VARSIZE(hs));
-		HS_FIXSIZE(out, hs_count);
-		HS_SETCOUNT(out, hs_count);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	/*
-	 * this is in effect a merge between hs and key_pairs, both of which are
-	 * already sorted by (keylen,key); we take keys from hs only
-	 */
+	it = HStoreIteratorInit(VARDATA(in));
 
-	for (i = j = 0; i < hs_count;)
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		int			difference;
 
-		if (j >= nkeys)
-			difference = -1;
-		else
+		if (skipNested == false)
 		{
-			int			skeylen = HS_KEYLEN(es, i);
-
-			if (skeylen == key_pairs[j].keylen)
-				difference = memcmp(HS_KEY(es, ps, i),
-									key_pairs[j].key,
-									key_pairs[j].keylen);
-			else
-				difference = (skeylen > key_pairs[j].keylen) ? 1 : -1;
+			Assert(v.type == hsvArray || v.type == hsvHash);
+			isHash = (v.type == hsvArray) ? false : true;
+			skipNested = true;
 		}
 
-		if (difference > 0)
-			++j;
-		else if (difference == 0)
-			++i, ++j;
-		else
+		if ((r == WHS_ELEM || r == WHS_KEY) && v.type == hsvString &&
+			i < a->array.nelems)
 		{
-			HS_COPYITEM(ed, bufd, pd,
-						HS_KEY(es, ps, i), HS_KEYLEN(es, i),
-						HS_VALLEN(es, i), HS_VALISNULL(es, i));
-			++outcount;
-			++i;
-		}
-	}
+			int diff;
 
-	HS_FINALIZE(out, outcount, bufd, pd);
+			if (isHash)
+			{
+				do {
+					diff = compareHStoreStringValue(&v, a->array.elems + i,
+													NULL);
 
-	PG_RETURN_POINTER(out);
-}
+					if (diff >= 0)
+						i++;
+				} while(diff > 0 && i < a->array.nelems);
+			}
+			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);
 
-PG_FUNCTION_INFO_V1(hstore_delete_hstore);
-Datum		hstore_delete_hstore(PG_FUNCTION_ARGS);
-Datum
-hstore_delete_hstore(PG_FUNCTION_ARGS)
+				continue;
+			}
+		}
+
+		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);
+	}
+
+	PG_RETURN_POINTER(out);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_delete_hstore);
+Datum		hstore_delete_hstore(PG_FUNCTION_ARGS);
+Datum
+hstore_delete_hstore(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HStore	   *hs2 = PG_GETARG_HS(1);
-	HStore	   *out = palloc(VARSIZE(hs));
-	int			hs_count = HS_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);
+	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));
 		PG_RETURN_POINTER(out);
 	}
 
-	/*
-	 * this is in effect a merge between hs and hs2, both of which are already
-	 * sorted by (keylen,key); we take keys from hs only; for equal keys, we
-	 * take the value from hs unless the values are equal
-	 */
+	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);
 
-	for (i = j = 0; i < hs_count;)
+	if (isHash1 == true && isHash2 == true)
 	{
-		int			difference;
+		bool			fin2 = false,
+						keyIsDef = false;
 
-		if (j >= hs2_count)
-			difference = -1;
-		else
+		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)
 		{
-			int			skeylen = HS_KEYLEN(es, i);
-			int			s2keylen = HS_KEYLEN(es2, j);
 
-			if (skeylen == s2keylen)
-				difference = memcmp(HS_KEY(es, ps, i),
-									HS_KEY(es2, ps2, j),
-									skeylen);
+			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);
+
+	if (r == WHS_BEGIN_ARRAY)
+	{
+		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
-				difference = (skeylen > s2keylen) ? 1 : -1;
+				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;
+		}
+
+		if (level == path_len - 1)
+		{
+			/* last level in path, skip all elem */
+			r = HStoreIteratorGet(it, &v, true);
+			Assert(r == WHS_ELEM);
+		}
+		else
+		{
+			res = deletePathDo(it, path_elems, path_nulls, path_len, st,
+							   level + 1);
+		}
+
+		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);
+		}
+
+		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);
+
+			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)
+			{
+				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;
+			}
+
+			pushHStoreValue(st, r, &k);
+			r = HStoreIteratorGet(it, &v, true);
+			Assert(r == WHS_VALUE);
+			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) /* just a string or null */
+	{
+		pushHStoreValue(st, r, &v);
+		res = (void*)0x01; /* dummy value */
+	}
+	else
+	{
+		elog(PANIC, "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;
+			}
+		}
+
+		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);
+	}
+
+	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(PANIC,"Could not convert to string");
+	}
+}
+
+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");
 
-		if (difference > 0)
-			++j;
-		else if (difference == 0)
+		res = pushHStoreValue(toState, r1, &v1);
+
+		for(;;)
 		{
-			int			svallen = HS_VALLEN(es, i);
-			int			snullval = HS_VALISNULL(es, i);
+			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);
+		}
 
-			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)))
+		while((r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
+		{
+			if (!(r2 == WHS_END_HASH || r2 == WHS_END_ARRAY))
 			{
-				HS_COPYITEM(ed, bufd, pd,
-							HS_KEY(es, ps, i), HS_KEYLEN(es, i),
-							svallen, snullval);
-				++outcount;
+				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);
+				}
 			}
-			++i, ++j;
+		}
+
+		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
 		{
-			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, 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(PANIC, "impossible state");
 	}
 
-	HS_FINALIZE(out, outcount, bufd, pd);
-
-	PG_RETURN_POINTER(out);
+	return res;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_concat);
-Datum		hstore_concat(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_replace);
+Datum		hstore_replace(PG_FUNCTION_ARGS);
 Datum
-hstore_concat(PG_FUNCTION_ARGS)
+hstore_replace(PG_FUNCTION_ARGS)
 {
-	HStore	   *s1 = PG_GETARG_HS(0);
-	HStore	   *s2 = PG_GETARG_HS(1);
-	HStore	   *out = palloc(VARSIZE(s1) + VARSIZE(s2));
-	char	   *ps1,
-			   *ps2,
-			   *bufd,
-			   *pd;
-	HEntry	   *es1,
-			   *es2,
-			   *ed;
-	int			s1idx;
-	int			s2idx;
-	int			s1count = 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);
+	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));
 		PG_RETURN_POINTER(out);
 	}
 
-	if (s2count == 0)
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &path_elems, &path_nulls, &path_len);
+
+	if (path_len == 0)
 	{
-		/* return a copy of the input, unchanged */
-		memcpy(out, s1, VARSIZE(s1));
-		HS_FIXSIZE(out, s1count);
-		HS_SETCOUNT(out, s1count);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	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)
+	if (HS_ROOT_COUNT(newval) == 0)
 	{
-		int			difference;
+		value.type = hsvNull;
+		value.size = sizeof(HEntry);
+	}
+	else
+	{
+		value.type = hsvBinary;
+		value.binary.data = VARDATA(newval);
+		value.binary.len = VARSIZE_ANY_EXHDR(newval);
+		value.size = value.binary.len + sizeof(HEntry);
+	}
 
-		if (s1idx >= s1count)
-			difference = 1;
-		else if (s2idx >= s2count)
-			difference = -1;
-		else
-		{
-			int			s1keylen = HS_KEYLEN(es1, s1idx);
-			int			s2keylen = HS_KEYLEN(es2, s2idx);
+	it = HStoreIteratorInit(VARDATA(in));
 
-			if (s1keylen == s2keylen)
-				difference = memcmp(HS_KEY(es1, ps1, s1idx),
-									HS_KEY(es2, ps2, s2idx),
-									s1keylen);
-			else
-				difference = (s1keylen > s2keylen) ? 1 : -1;
-		}
+	res = replacePathDo(&it, path_elems, path_nulls, path_len, &st, 0, &value);
 
-		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;
-		}
+	if (res == NULL)
+	{
+		SET_VARSIZE(out, VARHDRSZ);
 	}
+	else
+	{
+		int				sz;
 
-	HS_FINALIZE(out, outcount, bufd, pd);
+		sz = compressHStore(res, VARDATA(out));
+		SET_VARSIZE(out, sz + 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)
+static HStoreValue*
+concatPathDo(HStoreIterator **it, Datum *path_elems,
+			 bool *path_nulls, int path_len,
+			 ToHStoreState  **st, int level, HStoreIterator	*toConcat)
 {
-	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;
+	HStoreValue v, *res = NULL;
+	int			r;
 
-	deconstruct_array(key_array,
-					  TEXTOID, -1, false, 'i',
-					  &key_datums, &key_nulls, &key_count);
+	r = HStoreIteratorGet(it, &v, false);
 
-	if (key_count == 0)
+	if (r == WHS_BEGIN_ARRAY)
 	{
-		aout = construct_empty_array(TEXTOID);
-		PG_RETURN_POINTER(aout);
-	}
+		int		idx, i;
+		uint32	n = v.array.nelems;
 
-	out_datums = palloc(sizeof(Datum) * key_count);
-	out_nulls = palloc(sizeof(bool) * key_count);
+		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;
+		}
 
-	for (i = 0; i < key_count; ++i)
-	{
-		text	   *key = (text *) DatumGetPointer(key_datums[i]);
-		int			idx;
+		if (idx > n)
+			idx = n;
 
-		if (key_nulls[i])
-			idx = -1;
-		else
-			idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ);
+		pushHStoreValue(st, r, &v);
 
-		if (idx < 0 || HS_VALISNULL(entries, idx))
+		for(i=0; i<n; i++)
 		{
-			out_nulls[i] = true;
-			out_datums[i] = (Datum) 0;
+			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);
+			}
 		}
-		else
+
+		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++)
 		{
-			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, &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);
+			}
 		}
-	}
 
-	aout = construct_md_array(out_datums, out_nulls,
-							  ARR_NDIM(key_array),
-							  ARR_DIMS(key_array),
-							  ARR_LBOUND(key_array),
-							  TEXTOID, -1, false, 'i');
+		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(PANIC, "impossible state");
+	}
 
-	PG_RETURN_POINTER(aout);
+	return res;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_slice_to_hstore);
-Datum		hstore_slice_to_hstore(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_deep_concat);
+Datum		hstore_deep_concat(PG_FUNCTION_ARGS);
 Datum
-hstore_slice_to_hstore(PG_FUNCTION_ARGS)
+hstore_deep_concat(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *ptr = STRPTR(hs);
-	ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(1);
-	HStore	   *out;
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(key_array, &nkeys);
-	Pairs	   *out_pairs;
-	int			bufsiz;
-	int			lastidx = 0;
-	int			i;
-	int			out_count = 0;
-
-	if (nkeys == 0)
+	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")));
+
+	if (HS_ROOT_COUNT(in) == 0 || HS_ROOT_COUNT(newval) == 0)
 	{
-		out = hstorePairs(NULL, 0, 0);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	out_pairs = palloc(sizeof(Pairs) * nkeys);
-	bufsiz = 0;
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &path_elems, &path_nulls, &path_len);
 
-	/*
-	 * 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.
-	 */
+	it1 = HStoreIteratorInit(VARDATA(in));
+	it2 = HStoreIteratorInit(VARDATA(newval));
 
-	for (i = 0; i < nkeys; ++i)
-	{
-		int			idx = hstoreFindKey(hs, &lastidx,
-									  key_pairs[i].key, key_pairs[i].keylen);
+	if (path_len == 0)
+		res = IteratorConcat(&it1, &it2, &st);
+	else
+		res = concatPathDo(&it1, path_elems, path_nulls, path_len, &st, 0, it2);
 
-		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;
-		}
+	if (res == NULL)
+	{
+		SET_VARSIZE(out, VARHDRSZ);
 	}
+	else
+	{
+		int				sz;
 
-	/*
-	 * we don't use uniquePairs here because we know that the pairs list is
-	 * already sorted and uniq'ed.
-	 */
+		if (res->type == hsvArray && res->array.nelems > 1)
+			res->array.scalar = false;
 
-	out = hstorePairs(out_pairs, out_count, bufsiz);
+		sz = compressHStore(res, VARDATA(out));
+		SET_VARSIZE(out, sz + VARHDRSZ);
+	}
 
 	PG_RETURN_POINTER(out);
 }
 
-
 PG_FUNCTION_INFO_V1(hstore_akeys);
 Datum		hstore_akeys(PG_FUNCTION_ARGS);
 Datum
 hstore_akeys(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	Datum	   *d;
-	ArrayType  *a;
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *base = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			i;
-
-	if (count == 0)
+	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))
 	{
 		a = construct_empty_array(TEXTOID);
 		PG_RETURN_POINTER(a);
 	}
 
-	d = (Datum *) palloc(sizeof(Datum) * count);
+	d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
 
-	for (i = 0; i < count; ++i)
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		text	   *item = cstring_to_text_with_len(HS_KEY(entries, base, i),
-													HS_KEYLEN(entries, i));
+		skipNested = true;
 
-		d[i] = PointerGetDatum(item);
+		if ((r == WHS_ELEM && v.type != hsvNull) || r == WHS_KEY)
+			d[i++] = PointerGetDatum(HStoreValueToText(&v));
 	}
 
-	a = construct_array(d, count,
+	a = construct_array(d, i,
 						TEXTOID, -1, false, 'i');
 
 	PG_RETURN_POINTER(a);
@@ -727,43 +2045,40 @@ Datum		hstore_avals(PG_FUNCTION_ARGS);
 Datum
 hstore_avals(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	Datum	   *d;
-	bool	   *nulls;
-	ArrayType  *a;
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *base = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			lb = 1;
-	int			i;
-
-	if (count == 0)
+	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))
 	{
 		a = construct_empty_array(TEXTOID);
 		PG_RETURN_POINTER(a);
 	}
 
-	d = (Datum *) palloc(sizeof(Datum) * count);
-	nulls = (bool *) palloc(sizeof(bool) * count);
+	d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
+	nulls = (bool *) palloc(sizeof(bool) * HS_ROOT_COUNT(hs));
 
-	for (i = 0; i < count; ++i)
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		if (HS_VALISNULL(entries, i))
-		{
-			d[i] = (Datum) 0;
-			nulls[i] = true;
-		}
-		else
-		{
-			text	   *item = cstring_to_text_with_len(HS_VAL(entries, base, i),
-													  HS_VALLEN(entries, i));
+		skipNested = true;
 
-			d[i] = PointerGetDatum(item);
-			nulls[i] = false;
+		if (r == WHS_ELEM || r == WHS_VALUE)
+		{
+			d[i] = PointerGetDatum(HStoreValueToText(&v));
+			nulls[i] = (DatumGetPointer(d[i]) == NULL) ? true : false;
+			i++;
 		}
 	}
 
-	a = construct_md_array(d, nulls, 1, &count, &lb,
+	a = construct_md_array(d, nulls, 1, &i, &lb,
 						   TEXTOID, -1, false, 'i');
 
 	PG_RETURN_POINTER(a);
@@ -773,44 +2088,53 @@ hstore_avals(PG_FUNCTION_ARGS)
 static ArrayType *
 hstore_to_array_internal(HStore *hs, int ndims)
 {
-	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;
+	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;
 
 	Assert(ndims < 3);
 
 	if (count == 0 || ndims == 0)
 		return construct_empty_array(TEXTOID);
 
-	out_size[0] = count * 2 / ndims;
+	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_datums = palloc(sizeof(Datum) * count * 2);
 	out_nulls = palloc(sizeof(bool) * count * 2);
 
-	for (i = 0; i < count; ++i)
-	{
-		text	   *key = cstring_to_text_with_len(HS_KEY(entries, base, i),
-												   HS_KEYLEN(entries, i));
+	it = HStoreIteratorInit(VARDATA(hs));
 
-		out_datums[i * 2] = PointerGetDatum(key);
-		out_nulls[i * 2] = false;
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
 
-		if (HS_VALISNULL(entries, i))
-		{
-			out_datums[i * 2 + 1] = (Datum) 0;
-			out_nulls[i * 2 + 1] = true;
-		}
-		else
+		switch(r)
 		{
-			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;
+			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;
 		}
 	}
 
@@ -850,222 +2174,526 @@ hstore_to_matrix(PG_FUNCTION_ARGS)
  * there was no explanatory comment in the original code. --AG)
  */
 
-static void
-setup_firstcall(FuncCallContext *funcctx, HStore *hs,
-				FunctionCallInfoData *fcinfo)
-{
-	MemoryContext oldcontext;
-	HStore	   *st;
+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;
+	}
+
+	while(st->level >= 0)
+	{
+		uint32	header;
 
-	oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+		v = NULL;
+		if (st->path[st->level].v.type != hsvBinary)
+		{
+			st->level--;
+			continue;
+		}
 
-	st = (HStore *) palloc(VARSIZE(hs));
-	memcpy(st, hs, VARSIZE(hs));
+		header = *(uint32*)st->path[st->level].v.binary.data;
 
-	funcctx->user_fctx = (void *) st;
+		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;
 
-	if (fcinfo)
-	{
-		TupleDesc	tupdesc;
+				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;
+					}
+				}
 
-		/* 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");
+				v = getHStoreValue(st->path[st->level].v.binary.data,
+								   HS_FLAG_ARRAY, ith);
+			}
+			else
+			{
+				st->level--;
+				continue;
+			}
+		}
+		else
+		{
+			elog(PANIC, "impossible state");
+		}
 
-		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+		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;
+		}
 	}
 
-	MemoryContextSwitchTo(oldcontext);
+	return v;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_skeys);
-Datum		hstore_skeys(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_svals_path);
+Datum		hstore_svals_path(PG_FUNCTION_ARGS);
 Datum
-hstore_skeys(PG_FUNCTION_ARGS)
+hstore_svals_path(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	HStoreValue			*v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, NULL);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	if ((v = getNextValsPath(st)) != NULL)
 	{
-		HEntry	   *entries = ARRPTR(hs);
-		text	   *item;
+		text	*item = HStoreValueToText(v);
 
-		item = cstring_to_text_with_len(HS_KEY(entries, STRPTR(hs), i),
-										HS_KEYLEN(entries, i));
-
-		SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
+		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);
+PG_FUNCTION_INFO_V1(hstore_hvals_path);
+Datum		hstore_hvals_path(PG_FUNCTION_ARGS);
 Datum
-hstore_svals(PG_FUNCTION_ARGS)
+hstore_hvals_path(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	HStoreValue 		*v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, NULL);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0),
+							 PG_GETARG_ARRAYTYPE_P(1), NULL);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	if ((v = getNextValsPath(st)) != NULL)
 	{
-		HEntry	   *entries = ARRPTR(hs);
+		HStore	   *item = HStoreValueToHStore(v);
 
-		if (HS_VALISNULL(entries, i))
-		{
-			ReturnSetInfo *rsi;
-
-			/* ugly ugly ugly. why no macro for this? */
-			(funcctx)->call_cntr++;
-			rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-			rsi->isDone = ExprMultipleResult;
-			PG_RETURN_NULL();
-		}
+		if (item == NULL)
+			SRF_RETURN_NEXT_NULL(funcctx);
 		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_contains);
-Datum		hstore_contains(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_each);
+Datum		hstore_each(PG_FUNCTION_ARGS);
 Datum
-hstore_contains(PG_FUNCTION_ARGS)
+hstore_each(PG_FUNCTION_ARGS)
 {
-	HStore	   *val = PG_GETARG_HS(0);
-	HStore	   *tmpl = PG_GETARG_HS(1);
-	bool		res = true;
-	HEntry	   *te = ARRPTR(tmpl);
-	char	   *tstr = STRPTR(tmpl);
-	HEntry	   *ve = ARRPTR(val);
-	char	   *vstr = STRPTR(val);
-	int			tcount = HS_COUNT(tmpl);
-	int			lastidx = 0;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	int					r;
+	HStoreValue			v;
 
-	/*
-	 * 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
-	 */
+	if (SRF_IS_FIRSTCALL())
+	{
+		funcctx = SRF_FIRSTCALL_INIT();
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	for (i = 0; res && i < tcount; ++i)
+	while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
 	{
-		int			idx = hstoreFindKey(val, &lastidx,
-									  HS_KEY(te, tstr, i), HS_KEYLEN(te, i));
+		Datum		res,
+					dvalues[2] = {0, 0};
+		bool		nulls[2] = {false, false};
+		text	   *item;
+		HeapTuple	tuple;
 
-		if (idx >= 0)
+		if (r == WHS_ELEM)
 		{
-			bool		nullval = HS_VALISNULL(te, i);
-			int			vallen = HS_VALLEN(te, i);
+			nulls[0] = true;
 
-			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;
+			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);
 		}
 		else
-			res = false;
-	}
+		{
+			continue;
+		}
 
-	PG_RETURN_BOOL(res);
-}
+		tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
+		res = HeapTupleGetDatum(tuple);
 
+		SRF_RETURN_NEXT(funcctx, PointerGetDatum(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)
-										));
+	SRF_RETURN_DONE(funcctx);
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_each);
-Datum		hstore_each(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_each_hstore);
+Datum		hstore_each_hstore(PG_FUNCTION_ARGS);
 Datum
-hstore_each(PG_FUNCTION_ARGS)
+hstore_each_hstore(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	int					r;
+	HStoreValue			v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, fcinfo);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
 	{
-		HEntry	   *entries = ARRPTR(hs);
-		char	   *ptr = STRPTR(hs);
 		Datum		res,
-					dvalues[2];
+					dvalues[2] = {0, 0};
 		bool		nulls[2] = {false, false};
 		text	   *item;
+		HStore		*hitem;
 		HeapTuple	tuple;
 
-		item = cstring_to_text_with_len(HS_KEY(entries, ptr, i),
-										HS_KEYLEN(entries, i));
-		dvalues[0] = PointerGetDatum(item);
+		if (r == WHS_ELEM)
+		{
+			nulls[0] = true;
 
-		if (HS_VALISNULL(entries, i))
+			hitem = HStoreValueToHStore(&v);
+			if (hitem == NULL)
+				nulls[1] = true;
+			else
+				dvalues[1] = PointerGetDatum(hitem);
+		}
+		else if (r == WHS_KEY)
 		{
-			dvalues[1] = (Datum) 0;
-			nulls[1] = true;
+			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);
 		}
 		else
 		{
-			item = cstring_to_text_with_len(HS_VAL(entries, ptr, i),
-											HS_VALLEN(entries, i));
-			dvalues[1] = PointerGetDatum(item);
+			continue;
 		}
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
@@ -1077,6 +2705,183 @@ hstore_each(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(PANIC, "impossible state");
+	}
+
+	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
@@ -1089,72 +2894,28 @@ Datum		hstore_cmp(PG_FUNCTION_ARGS);
 Datum
 hstore_cmp(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs1 = PG_GETARG_HS(0);
-	HStore	   *hs2 = PG_GETARG_HS(1);
-	int			hcount1 = HS_COUNT(hs1);
-	int			hcount2 = HS_COUNT(hs2);
-	int			res = 0;
+	HStore	   		*hs1 = PG_GETARG_HS(0);
+	HStore	   		*hs2 = PG_GETARG_HS(1);
+	int				res;
 
-	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) || HS_ISEMPTY(hs2))
 	{
-		/* 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(hs1))
 		{
-			if (len1 > len2)
-				res = 1;
-			else if (len1 < len2)
-				res = -1;
-			else if (hcount1 > hcount2)
-				res = 1;
-			else if (hcount2 > hcount1)
-				res = -1;
+			if (HS_ISEMPTY(hs2))
+				res = 0;
 			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;
-				}
-			}
+				res = -1;
 		}
 		else
 		{
-			res = (res > 0) ? 1 : -1;
+			res = 1;
 		}
 	}
+	else
+	{
+		res = compareHStoreBinaryValue(VARDATA(hs1), VARDATA(hs2));
+	}
 
 	/*
 	 * this is a btree support function; this is one of the few places where
@@ -1248,17 +3009,61 @@ 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
new file mode 100644
index 0000000..ab9f67e
--- /dev/null
+++ b/contrib/hstore/hstore_scan.l
@@ -0,0 +1,311 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
+
diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql
index 9518f56..abf3c0e 100644
--- a/contrib/hstore/sql/hstore.sql
+++ b/contrib/hstore/sql/hstore.sql
@@ -39,6 +39,9 @@ select 'aa=>"bb" ,cc=>dd'::hstore;
 select 'aa=>null'::hstore;
 select 'aa=>NuLl'::hstore;
 select 'aa=>"NuLl"'::hstore;
+select 'aa=>nul'::hstore;
+select 'aa=>NuL'::hstore;
+select 'aa=>"NuL"'::hstore;
 
 select e'\\=a=>q=w'::hstore;
 select e'"=a"=>q\\=w'::hstore;
@@ -213,7 +216,7 @@ select hstore(v) from testhstore1 v;
 select hstore(null::testhstore0);
 select hstore(null::testhstore1);
 select pg_column_size(hstore(v))
-         = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore)
+         = pg_column_size('a=>1, b=>"foo", c=>1.2, d=>3, e=>0'::hstore)
   from testhstore1 v;
 select populate_record(v, hstore('c', '3.45')) from testhstore1 v;
 select populate_record(v, hstore('d', '3.45')) from testhstore1 v;
@@ -299,6 +302,8 @@ select count(*) from testhstore where h ? 'public';
 select count(*) from testhstore where h ?| ARRAY['public','disabled'];
 select count(*) from testhstore where h ?& ARRAY['public','disabled'];
 
+RESET enable_seqscan;
+
 drop index hidx;
 create index hidx on testhstore using gin (h);
 set enable_seqscan=off;
@@ -310,6 +315,8 @@ select count(*) from testhstore where h ? 'public';
 select count(*) from testhstore where h ?| ARRAY['public','disabled'];
 select count(*) from testhstore where h ?& ARRAY['public','disabled'];
 
+RESET enable_seqscan;
+
 select count(*) from (select (each(h)).key from testhstore) as wow ;
 select key, count(*) from (select (each(h)).key from testhstore) as wow group by key order by count desc, key;
 
@@ -323,6 +330,9 @@ select count(*) from (select h from (select * from testhstore union all select *
 select distinct * from (values (hstore '' || ''),('')) v(h);
 set enable_sort = true;
 
+RESET enable_hashagg;
+RESET enable_sort;
+
 -- btree
 drop index hidx;
 create index hidx on testhstore using btree (h);
@@ -331,6 +341,18 @@ set enable_seqscan=off;
 select count(*) from testhstore where h #># 'p=>1';
 select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
 
+--gin hash
+drop index hidx;
+create index hidx on testhstore using gin (h gin_hstore_hash_ops);
+set enable_seqscan=off;
+
+select count(*) from testhstore where h @> 'wait=>NULL';
+select count(*) from testhstore where h @> 'wait=>CC';
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+
+RESET enable_seqscan;
+drop index hidx;
+
 -- json
 select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
diff --git a/contrib/hstore/sql/nested.sql b/contrib/hstore/sql/nested.sql
new file mode 100644
index 0000000..a998650
--- /dev/null
+++ b/contrib/hstore/sql/nested.sql
@@ -0,0 +1,480 @@
+
+SELECT 'ff => {a=>12, b=>16}'::hstore;
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123'::hstore;
+
+SELECT 'aa => {a,aaa}, qq=>{ a=>12, b=>16 , c=> { c1, c2}, d=>{d1=>d1, d2=>d2, d1=>d3} }'::hstore;
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+
+SELECT 'ff => {a,aaa}'::hstore;
+
+
+select 'null'::hstore;
+select '{null}'::hstore;
+select ''::hstore;
+select '{}'::hstore;
+
+--test optional outer braces
+SELECT	'a=>1'::hstore;
+SELECT	'{a=>1}'::hstore;
+SELECT	'{a,b}'::hstore;
+SELECT	'{a,{b}}'::hstore;
+SELECT	'{{a},b}'::hstore;
+SELECT	'{a,{b},{c}}'::hstore;
+SELECT	'{{a},{b},c}'::hstore;
+SELECT	'{{a},b,{c}}'::hstore;
+SELECT	'{a,{b=>1}}'::hstore;
+SELECT	'{{a},{b=>1}}'::hstore;
+SELECT	'a'::hstore;
+SELECT	'{a}'::hstore;
+SELECT	''::hstore;
+SELECT	'{}'::hstore;
+
+--nested json
+
+SELECT	hstore_to_json('a=>1');
+SELECT	hstore_to_json('{a=>1}');
+SELECT	hstore_to_json('{a,b}');
+SELECT	hstore_to_json('{a,{b}}');
+SELECT	hstore_to_json('{{a},b}');
+SELECT	hstore_to_json('{a,{b},{c}}');
+SELECT	hstore_to_json('{{a},{b},c}');
+SELECT	hstore_to_json('{{a},b,{c}}');
+SELECT	hstore_to_json('{a,{b=>1}}');
+SELECT	hstore_to_json('{{a},{b=>1}}');
+SELECT	hstore_to_json('{{a},{b=>1},{c}}');
+SELECT	hstore_to_json('a');
+SELECT	hstore_to_json('{a}');
+SELECT	hstore_to_json('');
+SELECT	hstore_to_json('{}');
+
+SELECT hstore_to_json('"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore);
+
+--
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'ff', 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'qq', 
+	   ('ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'Y') IS NULL AS t, 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'x'; 
+
+SELECT '[ a, b, c, d]'::hstore -> 'a';
+--
+
+CREATE TABLE testtype (i int, h hstore, a int[], j json);
+INSERT INTO testtype VALUES (1, 'a=>1', '{1,2,3}', '{"x": 2}');
+
+SELECT populate_record(v, 'i=>2'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, a=>{7,8,9}'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}'::hstore) FROM testtype v;
+
+--complex delete
+
+SELECT 'b=>{a,c}'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>1'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+SELECT '[2,3,a]'::hstore - 'a'::text;
+SELECT '[a,2,3,a]'::hstore - 'a'::text;
+SELECT '[a,a]'::hstore - 'a'::text;
+SELECT '[a]'::hstore - 'a'::text;
+SELECT 'a=>1'::hstore - 'a'::text;
+SELECT ''::hstore - 'a'::text;
+
+SELECT '{a, 1 , b,2, c,3}'::hstore - ARRAY['d','b'];
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>23'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>{1,2}'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'a=>{1,2}'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v=>23'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,23]'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,{1,2}]'::hstore;
+
+--joining
+
+SELECT 'aa=>1 , b=>2, cq=>3'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+SELECT '{aa,1 , b,2, cq,3}'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+SELECT  'x'::hstore || 'a=>"1"':: hstore;
+
+--slice
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['b','c']);
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+SELECT slice_array(hstore 'aa=>1, b=>{2=>1}, c=>{1,2}', ARRAY['b','c']);
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['g','h','i']);
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['b','c']);
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+SELECT slice(hstore '{aa=>1, b=>{2=>1}, c=>{1,2}}', ARRAY['b','c']);
+
+--to array
+SELECT %% 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL';
+SELECT %% '{aa,1, cq,l, b,g, fg,NULL}';
+SELECT hstore_to_matrix( 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL');
+SELECT hstore_to_matrix( '{aa,1, cq,l, b,g, fg,NULL}');
+
+
+--contains
+SELECT 'a=>b'::hstore @> 'a=>b, c=>b';
+SELECT 'a=>b, c=>b'::hstore @> 'a=>b';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{2,1}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1=>2}';
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1=>2}';
+SELECT '{a,b}'::hstore @> '{a,b, c,b}';
+SELECT '{a,b, c,b}'::hstore @> '{a,b}';
+SELECT '{a,b, c,{1,2}}'::hstore @> '{a,{1,2}}';
+SELECT '{a,b, c,{1,2}}'::hstore @> '{b,{1,2}}';
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1}';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{2}';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{3}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{c=>3}}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4}}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},3}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},1}';
+
+-- %>
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'n';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'a';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'b';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'c';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd' %> '1';
+
+SELECT '[1,2,3,{a,b}]'::hstore %> '1';
+SELECT '["1",2,3,{a,b}]'::hstore %> '1';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 0;
+
+-- ->
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 0;
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -6;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -1;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -6;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -1;
+
+-- #>
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{a}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -4}';
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{0}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{3}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4,5}';
+
+-- #%>
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{a}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -4}';
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{0}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{3}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4,5}';
+
+-- ?
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 0;
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -6;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -1;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -6;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -1;
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{0}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{a}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{b}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 0}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 1}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 2}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 3}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -1}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -2}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -3}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -4}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -5}'::text[];
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{0}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{3}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4,5}'::text[];
+
+--deep delete
+
+SELECT 'a=>1'::hstore #- '{x}';
+SELECT 'a=>1'::hstore #- '{a}';
+SELECT 'a=>1'::hstore #- '{NULL}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c}';
+SELECT 'a=>1'::hstore #- '{x,1}';
+SELECT 'a=>1'::hstore #- '{a,1}';
+SELECT 'a=>1'::hstore #- '{NULL,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c,1}';
+
+SELECT '[a]'::hstore #- '{2}';
+SELECT '[a]'::hstore #- '{1}';
+SELECT '[a]'::hstore #- '{0}';
+SELECT '[a]'::hstore #- '{-1}';
+SELECT '[a]'::hstore #- '{-2}';
+
+SELECT '[a,b,c]'::hstore #- '{3}';
+SELECT '[a,b,c]'::hstore #- '{2}';
+SELECT '[a,b,c]'::hstore #- '{1}';
+SELECT '[a,b,c]'::hstore #- '{0}';
+SELECT '[a,b,c]'::hstore #- '{-1}';
+SELECT '[a,b,c]'::hstore #- '{-2}';
+SELECT '[a,b,c]'::hstore #- '{-3}';
+SELECT '[a,b,c]'::hstore #- '{-4}';
+
+SELECT '[a,b,c]'::hstore #- '{0,0}';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{x}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{a}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d}';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}' #- '{b, -1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 2}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, -2}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}' #- '{d, 1, 0}';
+
+-- delete(int)
+
+SELECT '[a,b,c]'::hstore - 3;
+SELECT '[a,b,c]'::hstore - 2;
+SELECT '[a,b,c]'::hstore - 1;
+SELECT '[a,b,c]'::hstore - 0;
+SELECT '[a,b,c]'::hstore - -1;
+SELECT '[a,b,c]'::hstore - -2;
+SELECT '[a,b,c]'::hstore - -3;
+SELECT '[a,b,c]'::hstore - -4;
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 3;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 2;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 1;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 0;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -1;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -2;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -3;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -4;
+
+--replace
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b,-1}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1,0}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,NULL,0}', '{1,2,3}');
+
+--deep concat
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'n=>not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'n=>not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{not_null}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'b=>{3,4}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b}', '{3,4}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '{4,5}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '4=>5');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d}', '2=>{4,5}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{NULL,1}', '4=>5');
+SELECT concat_path('x'::hstore, '{}'::text[], 'a=>"1"':: hstore);
+
+--cast 
+
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::text)::hstore AS err;
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json)::hstore AS ok;
+
+--hvals
+
+SELECT q->'tags' FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore) AS q;
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+
+--svals path
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+
+--each
+
+SELECT * FROM each('a=>b, c=>cc'::hstore) AS q;
+SELECT * FROM each('[a, b, c, cc]'::hstore) AS q;
+SELECT * FROM each('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+
+SELECT * FROM each_hstore('a=>b, c=>cc'::hstore) AS q;
+SELECT * FROM each_hstore('[a, b, c, cc]'::hstore) AS q;
+SELECT * FROM each_hstore('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+
+--decoration
+
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]]'::hstore AS h, '[a, {b=>c}, [c, d, e]]'::hstore AS a;
+
+SET hstore.pretty_print = true;
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]], e=>[1,2,3,4], f=>g, g=>j'::hstore AS h, 
+	   '[a, {b=>c, c=>d}, [c, d, e, [1,2], h, {f=>g, g=>f}]]'::hstore AS a;
+RESET hstore.pretty_print;
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore);
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, array_curly_braces := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, loose := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, array_curly_braces := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, array_curly_braces := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true, loose := true);
diff --git a/contrib/hstore/sql/types.sql b/contrib/hstore/sql/types.sql
new file mode 100644
index 0000000..b441f15
--- /dev/null
+++ b/contrib/hstore/sql/types.sql
@@ -0,0 +1,152 @@
+SELECT '"foo"=>true'::hstore;
+SELECT 'foo=>true'::hstore;
+SELECT '"true"=>true'::hstore;
+SELECT 'true=>true'::hstore;
+SELECT '"t"=>true'::hstore;
+SELECT 't=>true'::hstore;
+SELECT '"false"=>true'::hstore;
+SELECT 'false=>true'::hstore;
+SELECT '"f"=>true'::hstore;
+SELECT 'f=>true'::hstore;
+
+SELECT '"foo"=>false'::hstore;
+SELECT 'foo=>false'::hstore;
+SELECT '"false"=>false'::hstore;
+SELECT 'false=>false'::hstore;
+SELECT '"t"=>false'::hstore;
+SELECT 't=>false'::hstore;
+SELECT '"false"=>false'::hstore;
+SELECT 'false=>false'::hstore;
+SELECT '"f"=>false'::hstore;
+SELECT 'f=>false'::hstore;
+
+SELECT '"1"=>x'::hstore;
+SELECT '1=>x'::hstore;
+SELECT 'foo=>1'::hstore;
+SELECT 'foo=>1.'::hstore;
+SELECT 'foo=>1.0'::hstore;
+SELECT 'foo=>1.01'::hstore;
+SELECT 'foo=>1.01e'::hstore;
+SELECT 'foo=>1.01e1'::hstore;
+SELECT 'foo=>1.01e+1'::hstore;
+SELECT 'foo=>1.01e-1'::hstore;
+SELECT 'foo=>.1'::hstore;
+SELECT 'foo=>.1e'::hstore;
+SELECT 'foo=>.1e1'::hstore;
+SELECT 'foo=>.1e+1'::hstore;
+SELECT 'foo=>.1e-1'::hstore;
+SELECT 'foo=>0.1e-1'::hstore;
+SELECT 'foo=>00.1e-1'::hstore;
+
+SELECT 'foo=>+1'::hstore;
+SELECT 'foo=>+1.'::hstore;
+SELECT 'foo=>+1.0'::hstore;
+SELECT 'foo=>+1.01'::hstore;
+SELECT 'foo=>+1.01e'::hstore;
+SELECT 'foo=>+1.01e1'::hstore;
+SELECT 'foo=>+1.01e+1'::hstore;
+SELECT 'foo=>+1.01e-1'::hstore;
+SELECT 'foo=>+.1'::hstore;
+SELECT 'foo=>+.1e'::hstore;
+SELECT 'foo=>+.1e1'::hstore;
+SELECT 'foo=>+.1e+1'::hstore;
+SELECT 'foo=>+.1e-1'::hstore;
+
+SELECT 'foo=>-1'::hstore;
+SELECT 'foo=>-1.'::hstore;
+SELECT 'foo=>-1.0'::hstore;
+SELECT 'foo=>-1.01'::hstore;
+SELECT 'foo=>-1.01e'::hstore;
+SELECT 'foo=>-1.01e1'::hstore;
+SELECT 'foo=>-1.01e+1'::hstore;
+SELECT 'foo=>-1.01e-1'::hstore;
+SELECT 'foo=>-.1'::hstore;
+SELECT 'foo=>-.1e'::hstore;
+SELECT 'foo=>-.1e1'::hstore;
+SELECT 'foo=>-.1e+1'::hstore;
+SELECT 'foo=>-.1e-1'::hstore;
+
+SELECT 'foo=>1e2000'::hstore;
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'foo';
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'bar';
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 0;
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 1;
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'foo';
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'bar';
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 0;
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 1;
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 0}';
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 1}';
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'foo';
+SELECT 'foo=>t, bar=>x'::hstore ?> 'bar';
+SELECT 'foo=>t, bar=>x'::hstore ?> 0;
+SELECT 'foo=>t, bar=>x'::hstore ?> 1;
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'foo';
+SELECT '[foo, t, bar, x]'::hstore ?> 'bar';
+SELECT '[foo, t, bar, x]'::hstore ?> 0;
+SELECT '[foo, t, bar, x]'::hstore ?> 1;
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 0}';
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 1}';
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'foo';
+SELECT 'foo=>f, bar=>x'::hstore ?> 'bar';
+SELECT 'foo=>f, bar=>x'::hstore ?> 0;
+SELECT 'foo=>f, bar=>x'::hstore ?> 1;
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'foo';
+SELECT '[foo, f, bar, x]'::hstore ?> 'bar';
+SELECT '[foo, f, bar, x]'::hstore ?> 0;
+SELECT '[foo, f, bar, x]'::hstore ?> 1;
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 0}';
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 1}';
+
+
+SELECT hstore_typeof('a=>b') AS hash;
+SELECT hstore_typeof('{a=>b}') AS hash;
+SELECT hstore_typeof('{a, b}') AS array;
+SELECT hstore_typeof('{{a=>b}}') AS array;
+SELECT hstore_typeof('[a, b]') AS array;
+SELECT hstore_typeof('') AS "NULL";
+SELECT hstore_typeof('NULL') AS "null";
+SELECT hstore_typeof('1.0') AS numeric;
+SELECT hstore_typeof('t') AS bool;
+SELECT hstore_typeof('f') AS bool;
+
+SELECT hstore('xxx', 't'::bool);
+SELECT hstore('xxx', 'f'::bool);
+
+SELECT hstore('xxx', 3.14);
+SELECT hstore('xxx', 3.14::numeric);
+SELECT hstore('xxx', '3.14'::numeric);
+
+SELECT hstore(NULL);
+SELECT hstore('NULL');
+
+SELECT hstore('t'::bool) AS "true", hstore('f'::bool) AS "false";
+
+SELECT hstore(3.14), hstore(3.14::numeric), hstore('3.14'::numeric);
+
+SELECT hstore('xxx', 'foo=>t, bar=>3.14, zzz=>xxx'::hstore);
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int2[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int4[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int8[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float4[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float8[]);
+SELECT array_to_hstore('{{1,1,f},{f,t,NULL}}'::bool[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::text[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::varchar[]);
+
+SELECT array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]);
+SELECT hstore('array', array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]));
+
+SELECT 'a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore;
+SELECT hstore_to_json('a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore);
+SELECT hstore_to_json_loose('a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore);
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index e5b9e66..4b854f9 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -12,7 +12,7 @@
    <productname>PostgreSQL</> also provides event triggers.  Unlike regular
    triggers, which are attached to a single table and capture only DML events,
    event triggers are global to a particular database and are capable of
-   capturing DDL events.
+   capturing DDL events or transaction commits.
   </para>
 
   <para>
@@ -29,8 +29,9 @@
      occurs in the database in which it is defined. Currently, the only
      supported events are
      <literal>ddl_command_start</>,
-     <literal>ddl_command_end</>
-     and <literal>sql_drop</>.
+     <literal>ddl_command_end</>,
+     <literal>sql_drop</>, and
+     <literal>transaction_commit</>.
      Support for additional events may be added in future releases.
    </para>
 
@@ -65,6 +66,15 @@
    </para>
 
    <para>
+    A <literal>transaction_commit</> trigger is called at the end of a
+    transaction, just before any deferred triggers are fired, unless
+    no data changes have been made by the transaction, or
+    <productname>PostgreSQL</> is running in Single-User mode. This is so
+    that you can recover from a badly specified <literal>transaction_commit</>
+    trigger.
+   </para>
+
+   <para>
      Event triggers (like other functions) cannot be executed in an aborted
      transaction.  Thus, if a DDL command fails with an error, any associated
      <literal>ddl_command_end</> triggers will not be executed.  Conversely,
@@ -77,8 +87,13 @@
    </para>
 
    <para>
-     For a complete list of commands supported by the event trigger mechanism,
-     see <xref linkend="event-trigger-matrix">.
+    A <literal>transaction_commit</> trigger is also not called in an
+    aborted transaction.
+   </para>
+
+   <para>
+     For a complete list of commands supported by the event trigger
+     mechanism, see <xref linkend="event-trigger-matrix">.
    </para>
 
    <para>
@@ -101,6 +116,11 @@
      to intercept. A common use of such triggers is to restrict the range of
      DDL operations which users may perform.
    </para>
+
+   <para>
+    <literal>transaction_commit</> triggers do not currently support
+    <literal>WHEN</literal> clauses.
+   </para>
   </sect1>
 
   <sect1 id="event-trigger-matrix">
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index fbe9543..a4ebfcd 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -8,57 +8,170 @@
  </indexterm>
 
  <para>
-  This module implements the <type>hstore</> data type for storing sets of
-  key/value pairs within a single <productname>PostgreSQL</> value.
+  This module implements the <type>hstore</> data type for storing arbitrarily
+  nested key/value pairs and arrays 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 and values are
-  simply text strings.
+  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.
  </para>
 
  <sect2>
   <title><type>hstore</> External Representation</title>
 
   <para>
-
    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:
+   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:
 
-<synopsis>
-k =&gt; v
-foo =&gt; bar, baz =&gt; whatever
-"1-a" =&gt; "anything at all"
-</synopsis>
+<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>
 
    The order of the pairs is not significant (and may not be reproduced on
-   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.
+   output).
   </para>
 
   <para>
-   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:
+   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:
 
 <programlisting>
 SELECT 'a=&gt;1,a=&gt;2'::hstore;
   hstore
 ----------
- "a"=&gt;"1"
+ "a"=&gt;1
 </programlisting>
   </para>
 
   <para>
-   A value (but not a key) can be an SQL <literal>NULL</>. For example:
+   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>:
 
 <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>.
+=% 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>
   </para>
 
   <note>
@@ -83,6 +196,36 @@ key =&gt; NULL
  </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>
 
   <para>
@@ -98,6 +241,7 @@ key =&gt; NULL
     <thead>
      <row>
       <entry>Operator</entry>
+      <entry>Returns</entry>
       <entry>Description</entry>
       <entry>Example</entry>
       <entry>Result</entry>
@@ -107,20 +251,111 @@ key =&gt; NULL
     <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>
@@ -128,62 +363,103 @@ key =&gt; NULL
 
      <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>t</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>
      </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>t</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 any of the specified keys?</entry>
       <entry><literal>'a=&gt;1,b=&gt;2'::hstore ?| ARRAY['b','c']</literal></entry>
-      <entry><literal>t</literal></entry>
+      <entry><literal>true</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>t</literal></entry>
+      <entry><literal>true</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>f</literal></entry>
+      <entry><literal>false</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>
+      <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>
      </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>
+      <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>
      </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>
@@ -191,6 +467,7 @@ key =&gt; NULL
 
      <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>
@@ -198,6 +475,7 @@ key =&gt; NULL
 
      <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>
@@ -208,12 +486,22 @@ key =&gt; NULL
   </table>
 
   <note>
-  <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>
+    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>
   </note>
 
@@ -246,7 +534,7 @@ key =&gt; NULL
       <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>
@@ -258,6 +546,14 @@ key =&gt; NULL
      </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>
       <entry>make single-item <type>hstore</></entry>
@@ -266,6 +562,54 @@ key =&gt; NULL
      </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>
       <entry>get <type>hstore</>'s keys as an array</entry>
@@ -306,6 +650,18 @@ b
      </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>
       <entry>get <type>hstore</>'s keys and values as an array of alternating
@@ -327,7 +683,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>{"a key": "1", "b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "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>
@@ -335,7 +691,15 @@ 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>{"a key": 1, "b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 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>
      </row>
 
      <row>
@@ -343,7 +707,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>
@@ -361,6 +725,20 @@ b
      </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>
       <entry>does <type>hstore</> contain key?</entry>
@@ -377,11 +755,35 @@ b
      </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>
@@ -389,7 +791,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>
@@ -397,14 +799,22 @@ 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 Examples section</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></entry>
      </row>
 
@@ -415,7 +825,8 @@ b
   <note>
    <para>
      The function <function>hstore_to_json</function> is used when an <type>hstore</type>
-     value is cast to <type>json</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>.
    </para>
   </note>
 
@@ -426,6 +837,14 @@ 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>
@@ -441,6 +860,13 @@ 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>
    <type>hstore</> also supports <type>btree</> or <type>hash</> indexes for
    the <literal>=</> operator. This allows <type>hstore</> columns to be
@@ -458,6 +884,155 @@ CREATE INDEX hidx ON testhstore USING HASH (h);
  </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>
 
   <para>
@@ -483,21 +1058,7 @@ INSERT INTO test VALUES (123, 'foo', 'bar');
 SELECT hstore(t) FROM test AS t;
                    hstore                    
 ---------------------------------------------
- "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  | 
+ "col1"=&gt;123, "col2"=&gt;"foo", "col3"=&gt;"bar"
 (1 row)
 </programlisting>
   </para>
@@ -567,11 +1128,14 @@ SELECT key, count(*) FROM
  <sect2>
   <title>Compatibility</title>
 
-  <para>
-   As of PostgreSQL 9.0, <type>hstore</> uses a different internal
-   representation than previous versions. This presents no obstacle for
+  <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
    dump/restore upgrades since the text representation (used in the dump) is
-   unchanged.
+   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.
   </para>
 
   <para>
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 356890d..f41d6fc 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -203,6 +203,7 @@ distprep:
 	$(MAKE) -C catalog	schemapg.h postgres.bki postgres.description postgres.shdescription
 	$(MAKE) -C replication	repl_gram.c repl_scanner.c
 	$(MAKE) -C utils	fmgrtab.c fmgroids.h errcodes.h
+	$(MAKE) -C utils/adt	jsonb_gram.c jsonb_scan.c
 	$(MAKE) -C utils/misc	guc-file.c
 	$(MAKE) -C utils/sort	qsort_tuple.c
 
@@ -320,6 +321,9 @@ maintainer-clean: distclean
 	      utils/fmgroids.h \
 	      utils/fmgrtab.c \
 	      utils/errcodes.h \
+	      utils/adt/jsonb_gram.c \
+	      utils/adt/jsonb_gram.h \
+	      utils/adt/jsonb_scan.c \
 	      utils/misc/guc-file.c \
 	      utils/sort/qsort_tuple.c
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 043d118..ca6d14c 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -796,3 +796,11 @@ CREATE OR REPLACE FUNCTION
 CREATE OR REPLACE FUNCTION
   json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
   RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'json_populate_recordset';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_record(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS anyelement LANGUAGE internal STABLE AS 'jsonb_populate_record';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_recordset(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'jsonb_populate_recordset';
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
 	tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
 	tsvector.o tsvector_op.o tsvector_parser.o \
 	txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-	rangetypes_typanalyze.o rangetypes_selfuncs.o
+	rangetypes_typanalyze.o rangetypes_selfuncs.o \
+	jsonb.o jsonb_support.o
 
 like.o: like.c like_match.c
 
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 21a2336..421049c 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -1259,7 +1259,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
 			pfree(outputstr);
 			break;
 		case TYPCATEGORY_JSON:
-			/* JSON will already be escaped */
+			/* JSON and JSONB will already be escaped */
 			outputstr = OidOutputFunctionCall(typoutputfunc, val);
 			appendStringInfoString(result, outputstr);
 			pfree(outputstr);
@@ -1387,7 +1387,7 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
 		tcategory = TYPCATEGORY_JSON_CAST;
 	else if (element_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (element_type == JSONOID)
+	else if (element_type == JSONOID || element_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(element_type);
@@ -1482,7 +1482,8 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
 			tcategory = TYPCATEGORY_ARRAY;
 		else if (tupdesc->attrs[i]->atttypid == RECORDOID)
 			tcategory = TYPCATEGORY_COMPOSITE;
-		else if (tupdesc->attrs[i]->atttypid == JSONOID)
+		else if (tupdesc->attrs[i]->atttypid == JSONOID || 
+				 tupdesc->attrs[i]->atttypid == JSONBOID)
 			tcategory = TYPCATEGORY_JSON;
 		else
 			tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
@@ -1608,7 +1609,7 @@ to_json(PG_FUNCTION_ARGS)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (val_type == JSONOID)
+	else if (val_type == JSONOID || val_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(val_type);
@@ -1702,7 +1703,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (val_type == JSONOID)
+	else if (val_type == JSONOID || val_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(val_type);
@@ -1804,12 +1805,15 @@ escape_json(StringInfo buf, const char *str)
 Datum
 json_typeof(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonTokenType tok;
 	char *type;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
+
 	/* Lex exactly one token from the input and check its type. */
 	json_lex(lex);
 	tok = lex_peek(lex);
@@ -1840,3 +1844,4 @@ json_typeof(PG_FUNCTION_ARGS)
 
 	PG_RETURN_TEXT_P(cstring_to_text(type));
 }
+
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
new file mode 100644
index 0000000..2b34a93
--- /dev/null
+++ b/src/backend/utils/adt/jsonb.c
@@ -0,0 +1,565 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.c
+ *		I/O for jsonb type 
+ *
+ * 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
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/json.h"
+#include "utils/jsonapi.h"
+#include "utils/jsonb.h"
+
+static size_t
+checkStringLen(size_t len)
+{
+	 if (len > JSONB_MAX_STRING_LEN)
+		  ereport(ERROR,
+					 (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+					  errmsg("string too long for jsonb string")));
+	 return len;
+}
+
+static Jsonb*
+dumpJsonb(JsonbValue *p)
+{
+	 uint32			 buflen;
+	 Jsonb			*out;
+
+	 if (p == NULL)
+	 {
+		  buflen = 0;
+		  out = palloc(VARHDRSZ);
+	 }
+	 else
+	 {
+		  buflen = VARHDRSZ + p->size;
+		  out = palloc(buflen);
+		  SET_VARSIZE(out, buflen);
+
+		  buflen = compressJsonb(p, VARDATA(out));
+	 }
+	 SET_VARSIZE(out, buflen + VARHDRSZ);
+
+	 return out;
+}
+
+typedef struct JsonbInState
+{
+	ToJsonbState	*state;
+	JsonbValue		*res;
+}	JsonbInState;
+
+
+/*
+ * for jsonb we always want the de-escaped value - that's what's in token 
+ */
+
+static void 
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+	JsonbValue		v;
+
+	v.size = sizeof(JEntry);
+
+	switch (tokentype)
+	{
+			
+	case JSON_TOKEN_STRING:
+		v.type = jbvString;
+		v.string.len = token ? checkStringLen(strlen(token)) : 0;
+		v.string.val = token ? pnstrdup(token, v.string.len) : NULL;
+		v.size += v.string.len;
+		break;
+	case JSON_TOKEN_NUMBER:
+		v.type = jbvNumeric;
+		v.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
+		v.size += VARSIZE_ANY(v.numeric) + sizeof(JEntry) /* alignment */;
+		break;
+	case JSON_TOKEN_TRUE:
+		v.type = jbvBool;
+		v.boolean = true;
+		break;
+	case JSON_TOKEN_FALSE:
+		v.type = jbvBool;
+		v.boolean = false;
+		break;
+	case JSON_TOKEN_NULL:
+		v.type = jbvNull;
+		break;
+	default: /* nothing else should be here in fact */
+		break;
+	}
+
+	if (_state->state == NULL)
+	{
+		/* single scalar */
+		JsonbValue	va;
+
+		va.type = jbvArray;
+		va.array.scalar = true;
+		va.array.nelems = 1;
+
+		_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, &va);
+		_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+		_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+	}
+	else
+	{
+		JsonbValue	*o = &_state->state->v;
+
+		switch(o->type)
+		{
+			case jbvArray:
+				_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+				break;
+			case jbvHash:
+				_state->res = pushJsonbValue(&_state->state, WJB_VALUE, &v);
+				break;
+			default:
+				elog(ERROR, "Wrong state");
+		}
+	}
+}
+
+static void
+jsonb_in_object_start(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_OBJECT, NULL);
+}
+
+static void
+jsonb_in_object_end(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_OBJECT, NULL);
+}
+
+static void
+jsonb_in_array_start(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, NULL);
+}
+
+static void
+jsonb_in_array_end(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+}
+
+static void
+jsonb_in_object_field_start(void *state, char *fname, bool isnull)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+	JsonbValue		v;
+
+	v.type = jbvString;
+	v.string.len = fname ? checkStringLen(strlen(fname)) : 0;
+	v.string.val = fname ? pnstrdup(fname, v.string.len) : NULL;
+	v.size = sizeof(JEntry) + v.string.len;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_KEY, &v);
+}
+
+Datum
+jsonb_in(PG_FUNCTION_ARGS)
+{
+	char	   		*json = PG_GETARG_CSTRING(0);
+	text	   		*result = cstring_to_text(json);
+	JsonLexContext 	*lex;
+	JsonbInState 	state;
+	JsonSemAction 	sem;
+
+	memset(&state, 0, sizeof(state));
+	memset(&sem, 0, sizeof(sem));
+	lex = makeJsonLexContext(result, true);
+
+	sem.semstate = (void *) &state;
+
+	sem.object_start = jsonb_in_object_start;
+	sem.array_start = jsonb_in_array_start;
+	sem.object_end = jsonb_in_object_end;
+	sem.array_end = jsonb_in_array_end;
+	sem.scalar = jsonb_in_scalar;
+	sem.object_field_start = jsonb_in_object_field_start;
+
+	pg_parse_json(lex, &sem);
+
+	/* after parsing, the item membar has the composed jsonn structure */
+	PG_RETURN_POINTER(dumpJsonb(state.res));
+}
+
+static void recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header);
+
+static void
+recvJsonbValue(StringInfo buf, JsonbValue *v, uint32 level, int c)
+{
+	 uint32  hentry = c & JENTRY_TYPEMASK;
+
+	 if (hentry == JENTRY_ISNULL)
+	 {
+		  v->type = jbvNull;
+		  v->size = sizeof(JEntry);
+	 }
+	 else if (hentry == JENTRY_ISOBJECT || hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	 {
+		  recvJsonb(buf, v, level + 1, (uint32)c);
+	 }
+	 else if (hentry == JENTRY_ISFALSE || hentry == JENTRY_ISTRUE)
+	 {
+		  v->type = jbvBool;
+		  v->size = sizeof(JEntry);
+		  v->boolean = (hentry == JENTRY_ISFALSE) ? false : true;
+	 }
+	 else if (hentry == JENTRY_ISNUMERIC)
+	 {
+		  v->type = jbvNumeric;
+		  v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv, PointerGetDatum(buf),
+																			Int32GetDatum(0), Int32GetDatum(-1)));
+		  v->size = sizeof(JEntry) * 2 + VARSIZE_ANY(v->numeric);
+	 }
+	 else if (hentry == JENTRY_ISSTRING)
+	 {
+		  v->type = jbvString;
+		  v->string.val = pq_getmsgtext(buf, c, &c);
+		  v->string.len = checkStringLen(c);
+		  v->size = sizeof(JEntry) + v->string.len;
+	 }
+	 else
+	 {
+		  elog(ERROR, "bogus input");
+	 }
+}
+
+static void
+recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header)
+{
+	 uint32  hentry;
+	 uint32  i;
+
+	 hentry = header & JENTRY_TYPEMASK;
+
+	 v->size = 3 * sizeof(JEntry);
+	 if (hentry == JENTRY_ISOBJECT)
+	 {
+		  v->type = jbvHash;
+		  v->hash.npairs = header & JB_COUNT_MASK;
+		  if (v->hash.npairs > 0)
+		  {
+				v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+				for(i=0; i<v->hash.npairs; i++)
+				{
+					 recvJsonbValue(buf, &v->hash.pairs[i].key, level, pq_getmsgint(buf, 4));
+					 if (v->hash.pairs[i].key.type != jbvString)
+						  elog(ERROR, "jsonb's key could be only a string");
+
+					 recvJsonbValue(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;
+				}
+
+				uniqueJsonbValue(v);
+		  }
+	 }
+	 else if (hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	 {
+		  v->type = jbvArray;
+		  v->array.nelems = header & JB_COUNT_MASK;
+		  v->array.scalar = (hentry == JENTRY_ISCALAR) ? true : false;
+
+		  if (v->array.scalar && v->array.nelems != 1)
+				elog(ERROR, "bogus input");
+
+		  if (v->array.nelems > 0)
+		  {
+				v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+				for(i=0; i<v->array.nelems; i++)
+				{
+					 recvJsonbValue(buf, v->array.elems + i, level, pq_getmsgint(buf, 4));
+					 v->size += v->array.elems[i].size;
+				}
+		  }
+	 }
+	 else
+	 {
+				elog(ERROR, "bogus input");
+	 }
+}
+
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	 StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
+	 JsonbValue v;
+
+	 recvJsonb(buf, &v, 0, pq_getmsgint(buf, 4));
+
+	 PG_RETURN_POINTER(dumpJsonb(&v));
+}
+
+static void
+putEscapedValue(StringInfo out, JsonbValue *v)
+{
+	 switch(v->type)
+	 {
+		  case jbvNull:
+				appendBinaryStringInfo(out, "null", 4);
+				break;
+		  case jbvString:
+				escape_json(out, pnstrdup(v->string.val, v->string.len));
+				break;
+		  case jbvBool:
+				if (v->boolean)
+					 appendBinaryStringInfo(out, "true", 4);
+				else
+					 appendBinaryStringInfo(out, "false", 5);
+				break;
+		  case jbvNumeric:
+				appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+				break;
+		  default:
+				elog(PANIC, "Unknown type");
+	 }
+}
+
+char*
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{
+	 bool			first = true;
+	 JsonbIterator  *it;
+	 int			type;
+	 JsonbValue	  	v;
+	 int			 level = 0;
+
+	 if (out == NULL)
+		  out = makeStringInfo();
+
+	 if (in == NULL)
+	 {
+		  appendStringInfoString(out, "");
+		  return out->data;
+	 }
+
+	 enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);
+
+	 it = JsonbIteratorInit(in);
+
+	 while((type = JsonbIteratorGet(&it, &v, false)) != 0)
+	 {
+reout:
+		  switch(type)
+		  {
+				case WJB_BEGIN_ARRAY:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+
+					 if (v.array.scalar == false)
+						 appendStringInfoChar(out, '[');
+					 level++;
+					 break;
+				case WJB_BEGIN_OBJECT:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+					 appendStringInfoCharMacro(out, '{');
+
+					 level++;
+					 break;
+				case WJB_KEY:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+
+					 putEscapedValue(out, &v);
+					 appendBinaryStringInfo(out, ": ", 2);
+
+					 type = JsonbIteratorGet(&it, &v, false);
+					 if (type == WJB_VALUE)
+					 {
+						  first = false;
+						  putEscapedValue(out, &v);
+					 }
+					 else
+					 {
+						  Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+						  goto reout;
+					 }
+					 break;
+				case WJB_ELEM:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 else
+						  first = false;
+
+					 putEscapedValue(out, &v);
+					 break;
+				case WJB_END_ARRAY:
+					 level--;
+					 if (v.array.scalar == false)
+						  appendStringInfoChar(out, ']');
+					 first = false;
+					 break;
+				case WJB_END_OBJECT:
+					 level--;
+					 appendStringInfoCharMacro(out, '}');
+					 first = false;
+					 break;
+				default:
+					 elog(PANIC, "Wrong flags");
+		  }
+	 }
+
+	 Assert(level == 0);
+
+	 return out->data;
+}
+
+Datum
+jsonb_out(PG_FUNCTION_ARGS)
+{
+	 Jsonb  *jb = PG_GETARG_JSONB(0);
+	 char	 *out;
+
+	 out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	 PG_RETURN_CSTRING(out);
+}
+
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	 Jsonb			 *in = PG_GETARG_JSONB(0);
+	 StringInfoData  buf;
+
+	 pq_begintypsend(&buf);
+
+	 if (JB_ISEMPTY(in))
+	 {
+		  pq_sendint(&buf, 0, 4);
+	 }
+	 else
+	 {
+		  JsonbIterator  *it;
+		  int				 type;
+		  JsonbValue	  v;
+		  uint32			 flag;
+		  bytea			  *nbuf;
+
+		  enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */);
+
+		  it = JsonbIteratorInit(VARDATA_ANY(in));
+
+		  while((type = JsonbIteratorGet(&it, &v, false)) != 0)
+		  {
+				switch(type)
+				{
+					 case WJB_BEGIN_ARRAY:
+						  flag = (v.array.scalar) ? JENTRY_ISCALAR : JENTRY_ISARRAY;
+						  pq_sendint(&buf, v.array.nelems | flag, 4);
+						  break;
+					 case WJB_BEGIN_OBJECT:
+						  pq_sendint(&buf, v.hash.npairs | JENTRY_ISOBJECT, 4);
+						  break;
+					 case WJB_KEY:
+						  pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+						  pq_sendtext(&buf, v.string.val, v.string.len);
+						  break;
+					 case WJB_ELEM:
+					 case WJB_VALUE:
+						  switch(v.type)
+						  {
+								case jbvNull:
+									 pq_sendint(&buf, JENTRY_ISNULL, 4);
+									 break;
+								case jbvString:
+									 pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+									 pq_sendtext(&buf, v.string.val, v.string.len);
+									 break;
+								case jbvBool:
+									 pq_sendint(&buf, (v.boolean) ? JENTRY_ISTRUE : JENTRY_ISFALSE, 4);
+									 break;
+								case jbvNumeric:
+									 nbuf = DatumGetByteaP(DirectFunctionCall1(numeric_send, NumericGetDatum(v.numeric)));
+									 pq_sendint(&buf, VARSIZE_ANY(nbuf) | JENTRY_ISNUMERIC, 4);
+									 pq_sendbytes(&buf, (char*)nbuf, VARSIZE_ANY(nbuf));
+									 break;
+								default:
+									 elog(PANIC, "Wrong type: %u", v.type);
+						  }
+						  break;
+					 case WJB_END_ARRAY:
+					 case WJB_END_OBJECT:
+						  break;
+					 default:
+						  elog(PANIC, "Wrong flags");
+				}
+		  }
+	 }
+
+	 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{
+	 Jsonb			 *in = PG_GETARG_JSONB(0);
+	 JsonbIterator   *it;
+	 JsonbValue	      v;
+	 char            *result;
+
+	 if (JB_ROOT_IS_OBJECT(in))
+		 result = "object";
+	 else if (JB_ROOT_IS_ARRAY(in) && ! JB_ROOT_IS_SCALAR(in))
+		 result = "array";
+	 else
+	 {
+		 Assert(JB_ROOT_IS_SCALAR(in));
+
+		 it = JsonbIteratorInit(VARDATA_ANY(in));
+		 /* 
+		  * a root scalar is stored as an array of one element, 
+		  * so we get the array and then its first (and only) member.
+		  */
+		 (void) JsonbIteratorGet(&it, &v, true);
+		 (void) JsonbIteratorGet(&it, &v, true);
+		 switch(v.type)
+		 {
+		 case jbvNull:
+			 result = "null";
+			 break;
+		 case jbvString:
+			 result = "string";
+			 break;
+		 case jbvBool:
+			 result = "boolean";
+			 break;
+		 case jbvNumeric:
+			 result = "number";
+			 break;
+		 default:
+			 elog(ERROR, "Wrong jsonb scalar type: %u", v.type);
+		 }
+	 }
+	 
+	PG_RETURN_TEXT_P(cstring_to_text(result));		 
+}
diff --git a/src/backend/utils/adt/jsonb_support.c b/src/backend/utils/adt/jsonb_support.c
new file mode 100644
index 0000000..f629bb2
--- /dev/null
+++ b/src/backend/utils/adt/jsonb_support.c
@@ -0,0 +1,1180 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb_support.c
+ *    Support functions for jsonb
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/adt/jsonb_support.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "utils/builtins.h"
+#include "utils/jsonb.h"
+
+/* turn a JsonbValue into a Jsonb */
+
+Jsonb *
+JsonbValueToJsonb(JsonbValue *v)
+{
+	Jsonb			*out;
+
+	if (v == NULL || v->type == jbvNull)
+	{
+		out = NULL;
+	}
+	else if (v->type == jbvString || v->type == jbvBool ||
+			 v->type == jbvNumeric)
+	{
+		ToJsonbState	*state = NULL;
+		JsonbValue		*res;
+		int				r;
+		JsonbValue		scalarArray;
+
+		scalarArray.type = jbvArray;
+		scalarArray.array.scalar = true;
+		scalarArray.array.nelems = 1;
+
+		pushJsonbValue(&state, WJB_BEGIN_ARRAY, &scalarArray);
+		pushJsonbValue(&state, WJB_ELEM, v);
+		res = pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+		out = palloc(VARHDRSZ + res->size);
+		SET_VARSIZE(out, VARHDRSZ + res->size);
+		r = compressJsonb(res, VARDATA(out));
+		Assert(r <= res->size);
+		SET_VARSIZE(out, r + VARHDRSZ);
+	}
+	else
+	{
+		out = palloc(VARHDRSZ + v->size);
+
+		Assert(v->type == jbvBinary);
+		SET_VARSIZE(out, VARHDRSZ + v->binary.len);
+		memcpy(VARDATA(out), v->binary.data, v->binary.len);
+	}
+
+	return out;
+}
+
+/*
+ * Sort and unique pairs in hash-like JsonbValue
+ */
+void
+uniqueJsonbValue(JsonbValue *v)
+{
+	bool    hasNonUniq = false;
+
+	Assert(v->type == jbvHash);
+
+	if (v->hash.npairs > 1)
+		qsort_arg(v->hash.pairs, v->hash.npairs, sizeof(*v->hash.pairs),
+				  compareJsonbPair, &hasNonUniq);
+
+	if (hasNonUniq)
+	{
+		JsonbPair	*ptr = v->hash.pairs + 1,
+					*res = v->hash.pairs;
+
+		while(ptr - v->hash.pairs < v->hash.npairs)
+		{
+			if (ptr->key.string.len == res->key.string.len &&
+				memcmp(ptr->key.string.val, res->key.string.val,
+				ptr->key.string.len) == 0)
+			{
+				v->size -= ptr->key.size + ptr->value.size;
+			}
+			else
+			{
+				res++;
+				if (ptr != res)
+					memcpy(res, ptr, sizeof(*res));
+			}
+			ptr++;
+		}
+
+		v->hash.npairs = res + 1 - v->hash.pairs;
+	}
+}
+
+/****************************************************************************
+ *                         Compare Functions                                *
+ ****************************************************************************/
+int
+compareJsonbStringValue(const void *a, const void *b, void *arg)
+{
+	const JsonbValue  *va = a;
+	const JsonbValue  *vb = b;
+	int					res;
+
+	Assert(va->type == jbvString);
+	Assert(vb->type == jbvString);
+
+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool*)arg = true;
+	}
+	else
+	{
+		res = (va->string.len > vb->string.len) ? 1 : -1;
+	}
+
+	return res;
+}
+
+int
+compareJsonbPair(const void *a, const void *b, void *arg)
+{
+	const 	JsonbPair *pa = a;
+	const 	JsonbPair *pb = b;
+	int 	res;
+
+	res = compareJsonbStringValue(&pa->key, &pb->key, arg);
+
+	/*
+	 * guarantee keeping order of equal pair. Unique algorithm will
+	 * prefer first element as value
+	 */
+
+	if (res == 0)
+		res = (pa->order > pb->order) ? -1 : 1;
+
+	return res;
+}
+
+int
+compareJsonbValue(JsonbValue *a, JsonbValue *b)
+{
+	if (a->type == b->type)
+	{
+		switch(a->type)
+		{
+			case jbvNull:
+				return 0;
+			case jbvString:
+				return compareJsonbStringValue(a, b, NULL);
+			case jbvBool:
+				if (a->boolean == b->boolean)
+					return 0;
+				return (a->boolean > b->boolean) ? 1 : -1;
+			case jbvNumeric:
+				return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+														 PointerGetDatum(a->numeric),
+														 PointerGetDatum(b->numeric)));
+			case jbvArray:
+				if (a->array.nelems == b->array.nelems)
+				{
+					int i, r;
+
+					for(i=0; i<a->array.nelems; i++)
+						if ((r = compareJsonbValue(a->array.elems + i, 
+												   b->array.elems + i)) != 0)
+							return r;
+
+					return 0;
+				}
+
+				return (a->array.nelems > b->array.nelems) ? 1 : -1;
+			case jbvHash:
+				if (a->hash.npairs == b->hash.npairs)
+				{
+					int i, r;
+
+					for(i=0; i<a->hash.npairs; i++)
+					{
+						if ((r = compareJsonbStringValue(&a->hash.pairs[i].key,
+														 &b->hash.pairs[i].key,
+														 NULL)) != 0)
+							return r;
+						if ((r = compareJsonbValue(&a->hash.pairs[i].value, 
+												   &b->hash.pairs[i].value)) != 0)
+							return r;
+					}
+
+					return 0;
+				}
+
+				return (a->hash.npairs > b->hash.npairs) ? 1 : -1;
+			case jbvBinary:
+				return compareJsonbBinaryValue(a->binary.data, b->binary.data);
+			default:
+				elog(PANIC, "unknown JsonbValue->type: %d", a->type);
+		}
+	}
+
+	return (a->type > b->type) ? 1 : -1;
+}
+
+int
+compareJsonbBinaryValue(char *a, char *b)
+{
+	JsonbIterator	*it1, *it2;
+	int				res = 0;
+
+	it1 = JsonbIteratorInit(a);
+	it2 = JsonbIteratorInit(b);
+
+	while(res == 0)
+	{
+		JsonbValue		v1, v2;
+		int				r1, r2;
+
+		r1 = JsonbIteratorGet(&it1, &v1, false);
+		r2 = JsonbIteratorGet(&it2, &v2, false);
+
+		if (r1 == r2)
+		{
+			if (r1 == 0)
+				break; /* equal */
+
+			if (v1.type == v2.type)
+			{
+				switch(v1.type)
+				{
+					case jbvString:
+						res = compareJsonbStringValue(&v1, &v2, NULL);
+						break;
+					case jbvBool:
+						if (v1.boolean == v2.boolean)
+							res = 0;
+						else
+							res = (v1.boolean > v2.boolean) ? 1 : -1;
+						break;
+					case jbvNumeric:
+						res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+												 PointerGetDatum(v1.numeric),
+												 PointerGetDatum(v2.numeric)));
+						break;
+					case jbvArray:
+						if (v1.array.nelems != v2.array.nelems)
+							res = (v1.array.nelems > v2.array.nelems) ? 1 : -1;
+						break;
+					case jbvHash:
+						if (v1.hash.npairs != v2.hash.npairs)
+							res = (v1.hash.npairs > v2.hash.npairs) ? 1 : -1;
+						break;
+					default:
+						break;
+				}
+			}
+			else
+			{
+				res = (v1.type > v2.type) ?  1 : -1; /* dummy order */
+			}
+		}
+		else
+		{
+			res = (r1 > r2) ? 1 : -1; /* dummy order */
+		}
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *                         find string key in hash or array                 *
+ ****************************************************************************/
+JsonbValue*
+findUncompressedJsonbValueByValue(char *buffer, uint32 flags, uint32 *lowbound, JsonbValue *key)
+{
+	uint32				header = *(uint32*)buffer;
+	static JsonbValue 	r;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) != 
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		JEntry	*array = (JEntry*)(buffer + sizeof(header));
+		char 	*data = (char*)(array + (header & JB_COUNT_MASK));
+		int 	i;
+
+		for(i=(lowbound) ? *lowbound : 0; i<(header & JB_COUNT_MASK); i++) {
+			JEntry	*e = array + i;
+
+			if (JBE_ISNULL(*e) && key->type == jbvNull)
+			{
+				r.type = jbvNull;
+				if (lowbound)
+					*lowbound = i;
+				r.size = sizeof(JEntry);
+
+				return &r;
+			}
+			else if (JBE_ISSTRING(*e) && key->type == jbvString )
+			{
+				if (key->string.len == JBE_LEN(*e) &&
+					memcmp(key->string.val, data + JBE_OFF(*e), 
+						   key->string.len) == 0)
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*e);
+					r.string.len = key->string.len;
+					r.size = sizeof(JEntry) + r.string.len;
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISBOOL(*e) && key->type == jbvBool)
+			{
+				if ((JBE_ISBOOL_TRUE(*e) && key->boolean == true) ||
+					(JBE_ISBOOL_FALSE(*e) && key->boolean == false))
+				{
+					r = *key;
+					r.size = sizeof(JEntry);
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISNUMERIC(*e) && key->type == jbvNumeric)
+			{
+				if (DatumGetBool(DirectFunctionCall2(numeric_eq, 
+								 PointerGetDatum(data + INTALIGN(JBE_OFF(*e))),
+								 PointerGetDatum(key->numeric))) == true)
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*e)));
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+		}
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		JEntry  *array = (JEntry*)(buffer + sizeof(header));
+		char    *data = (char*)(array + (header & JB_COUNT_MASK) * 2);
+		uint32	stopLow = lowbound ? *lowbound : 0,
+				stopHigh = (header & JB_COUNT_MASK),
+				stopMiddle;
+
+		if (key->type != jbvString)
+			return NULL;
+
+		while (stopLow < stopHigh)
+		{
+			int		difference;
+			JEntry	*e;
+
+			stopMiddle = stopLow + (stopHigh - stopLow) / 2;
+
+			e = array + stopMiddle * 2;
+
+			if (key->string.len == JBE_LEN(*e))
+				difference = memcmp(data + JBE_OFF(*e), key->string.val,
+									key->string.len);
+			else
+				difference = (JBE_LEN(*e) > key->string.len) ? 1 : -1;
+
+			if (difference == 0)
+			{
+				JEntry	*v = e + 1;
+
+				if (lowbound)
+					*lowbound = stopMiddle + 1;
+
+				if (JBE_ISSTRING(*v))
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*v);
+					r.string.len = JBE_LEN(*v);
+					r.size = sizeof(JEntry) + r.string.len;
+				}
+				else if (JBE_ISBOOL(*v))
+				{
+					r.type = jbvBool;
+					r.boolean = (JBE_ISBOOL_TRUE(*v)) ? true : false;
+					r.size = sizeof(JEntry);
+				}
+				else if (JBE_ISNUMERIC(*v))
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*v)));
+					r.size = 2*sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+				}
+				else if (JBE_ISNULL(*v))
+				{
+					r.type = jbvNull;
+					r.size = sizeof(JEntry);
+				}
+				else
+				{
+					r.type = jbvBinary;
+					r.binary.data = data + INTALIGN(JBE_OFF(*v));
+					r.binary.len = JBE_LEN(*v) - 
+									(INTALIGN(JBE_OFF(*v)) - JBE_OFF(*v));
+					r.size = 2*sizeof(JEntry) + r.binary.len;
+				}
+
+				return &r;
+			}
+			else if (difference < 0)
+			{
+				stopLow = stopMiddle + 1;
+			}
+			else
+			{
+				stopHigh = stopMiddle;
+			}
+		}
+
+		if (lowbound)
+			*lowbound = stopLow;
+	}
+
+	return NULL;
+}
+
+JsonbValue*
+findUncompressedJsonbValue(char *buffer, uint32 flags, uint32 *lowbound,
+						   char *key, uint32 keylen)
+{
+	JsonbValue	v;
+
+	if (key == NULL)
+	{
+		v.type = jbvNull;
+	}
+	else
+	{
+		v.type = jbvString;
+		v.string.val = key;
+		v.string.len = keylen;
+	}
+
+	return findUncompressedJsonbValueByValue(buffer, flags, lowbound, &v);
+}
+
+JsonbValue*
+getJsonbValue(char *buffer, uint32 flags, int32 i)
+{
+	uint32				header = *(uint32*)buffer;
+	static JsonbValue	r;
+	JEntry				*array, *e;
+	char				*data;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) !=
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (i >= 0)
+	{
+		if (i >= (header & JB_COUNT_MASK))
+			return NULL;
+	}
+	else
+	{
+		if (-i > (header & JB_COUNT_MASK))
+			return NULL;
+
+		i = (header & JB_COUNT_MASK) + i;
+	}
+
+	array = (JEntry*)(buffer + sizeof(header));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		e = array + i;
+		data = (char*)(array + (header & JB_COUNT_MASK));
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		e = array + i * 2 + 1;
+		data = (char*)(array + (header & JB_COUNT_MASK) * 2);
+	}
+	else
+	{
+		return NULL;
+	}
+
+	if (JBE_ISSTRING(*e))
+	{
+		r.type = jbvString;
+		r.string.val = data + JBE_OFF(*e);
+		r.string.len = JBE_LEN(*e);
+		r.size = sizeof(JEntry) + r.string.len;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		r.type = jbvBool;
+		r.boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		r.size = sizeof(JEntry);
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		r.type = jbvNumeric;
+		r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*e)));
+		r.size = 2*sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		r.type = jbvNull;
+		r.size = sizeof(JEntry);
+	}
+	else
+	{
+		r.type = jbvBinary;
+		r.binary.data = data + INTALIGN(JBE_OFF(*e));
+		r.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		r.size = r.binary.len + 2*sizeof(JEntry);
+	}
+
+	return &r;
+}
+
+/****************************************************************************
+ *                         Walk on tree representation of jsonb             *
+ ****************************************************************************/
+static void
+walkUncompressedJsonbDo(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg, uint32 level) 
+{
+	int i;
+
+	switch(v->type)
+	{
+		case jbvArray:
+			cb(cb_arg, v, WJB_BEGIN_ARRAY, level);
+			for(i=0; i<v->array.nelems; i++)
+			{
+				if (v->array.elems[i].type == jbvNull ||
+					v->array.elems[i].type == jbvString ||
+					v->array.elems[i].type == jbvBool ||
+					v->array.elems[i].type == jbvNumeric ||
+					v->array.elems[i].type == jbvBinary)
+					cb(cb_arg, v->array.elems + i, WJB_ELEM, level);
+				else
+					walkUncompressedJsonbDo(v->array.elems + i, cb, cb_arg,
+											level + 1);
+			}
+			cb(cb_arg, v, WJB_END_ARRAY, level);
+			break;
+		case jbvHash:
+			cb(cb_arg, v, WJB_BEGIN_OBJECT, level);
+
+			for(i=0; i<v->hash.npairs; i++)
+			{
+				cb(cb_arg, &v->hash.pairs[i].key, WJB_KEY, level);
+				
+				if (v->hash.pairs[i].value.type == jbvNull ||
+					v->hash.pairs[i].value.type == jbvString ||
+					v->hash.pairs[i].value.type == jbvBool ||
+					v->hash.pairs[i].value.type == jbvNumeric ||
+					v->hash.pairs[i].value.type == jbvBinary)
+					cb(cb_arg, &v->hash.pairs[i].value, WJB_VALUE, level);
+				else 
+					walkUncompressedJsonbDo(&v->hash.pairs[i].value, cb, cb_arg,
+											level + 1);
+			}
+
+			cb(cb_arg, v, WJB_END_OBJECT, level);
+			break;
+		default:
+			elog(PANIC, "impossible JsonbValue->type: %d", v->type);
+	}
+}
+
+void
+walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg)
+{
+	if (v)
+		walkUncompressedJsonbDo(v, cb, cb_arg, 0);
+}
+
+/****************************************************************************
+ *                         Iteration over binary jsonb                      *
+ ****************************************************************************/
+static void
+parseBuffer(JsonbIterator *it, char *buffer)
+{
+	uint32	header = *(uint32*)buffer;
+
+	it->type = header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT);
+	it->nelems = header & JB_COUNT_MASK;
+	it->buffer = buffer;
+
+
+	buffer += sizeof(uint32);
+	it->array = (JEntry*)buffer;
+
+	it->state = jbi_start;
+
+	switch(it->type)
+	{
+		case JB_FLAG_ARRAY:
+			it->data = buffer + it->nelems * sizeof(JEntry);
+			it->isScalar = (header & JB_FLAG_SCALAR) ? true : false;
+			Assert(it->isScalar == false || it->nelems == 1);
+			break;
+		case JB_FLAG_OBJECT:
+			it->data = buffer + it->nelems * sizeof(JEntry) * 2;
+			break;
+		default:
+			elog(PANIC, "impossible type: %08x", it->type);
+	}
+}
+
+JsonbIterator*
+JsonbIteratorInit(char *buffer)
+{
+	JsonbIterator	*it = palloc(sizeof(*it));
+
+	parseBuffer(it, buffer);
+	it->next = NULL;
+
+	return it;
+}
+
+static bool
+formAnswer(JsonbIterator **it, JsonbValue *v, JEntry *e, bool skipNested)
+{
+	if (JBE_ISSTRING(*e))
+	{
+		v->type = jbvString;
+		v->string.val = (*it)->data + JBE_OFF(*e);
+		v->string.len = JBE_LEN(*e);
+		v->size = sizeof(JEntry) + v->string.len;
+
+		return false;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		v->type = jbvBool;
+		v->boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		v->type = jbvNumeric;
+		v->numeric = (Numeric)((*it)->data + INTALIGN(JBE_OFF(*e)));
+		v->size = 2*sizeof(JEntry) + VARSIZE_ANY(v->numeric);
+
+		return false;
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (skipNested)
+	{
+		v->type = jbvBinary;
+		v->binary.data = (*it)->data + INTALIGN(JBE_OFF(*e));
+		v->binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		v->size = v->binary.len + 2*sizeof(JEntry);
+
+		return false;
+	}
+	else
+	{
+		JsonbIterator *nit = palloc(sizeof(*nit));
+
+		parseBuffer(nit, (*it)->data + INTALIGN(JBE_OFF(*e)));
+		nit->next = *it;
+		*it = nit;
+
+		return true;
+	}
+}
+
+static JsonbIterator*
+up(JsonbIterator *it)
+{
+	JsonbIterator *v = it->next;
+
+	pfree(it);
+
+	return v;
+}
+
+int
+JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested)
+{
+	int res;
+
+	if (*it == NULL)
+		return 0;
+
+	/*
+	 * Encode all possible states by one integer. That's possible
+	 * because enum members of JsonbIterator->state uses different bits
+	 * than JB_FLAG_ARRAY/JB_FLAG_OBJECT. See definition of JsonbIterator
+	 */
+
+	switch((*it)->type | (*it)->state)
+	{
+		case JB_FLAG_ARRAY | jbi_start:
+			(*it)->state = jbi_elem;
+			(*it)->i = 0;
+			v->type = jbvArray;
+			v->array.nelems = (*it)->nelems;
+			res = WJB_BEGIN_ARRAY;
+			v->array.scalar = (*it)->isScalar;
+			break;
+		case JB_FLAG_ARRAY | jbi_elem:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_ARRAY;
+			}
+			else if (formAnswer(it, v, &(*it)->array[ (*it)->i++ ], skipNested))
+			{
+				res = JsonbIteratorGet(it, v, skipNested);
+			}
+			else
+			{
+				res = WJB_ELEM;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_start:
+			(*it)->state = jbi_key;
+			(*it)->i = 0;
+			v->type = jbvHash;
+			v->hash.npairs = (*it)->nelems;
+			res = WJB_BEGIN_OBJECT;
+			break;
+		case JB_FLAG_OBJECT | jbi_key:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_OBJECT;
+			}
+			else
+			{
+				formAnswer(it, v, &(*it)->array[ (*it)->i * 2 ], false);
+				(*it)->state = jbi_value;
+				res = WJB_KEY;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_value:
+			(*it)->state = jbi_key;
+			if (formAnswer(it, v, &(*it)->array[ ((*it)->i++) * 2 + 1], skipNested))
+				res = JsonbIteratorGet(it, v, skipNested);
+			else
+				res = WJB_VALUE;
+			break;
+		default:
+			elog(PANIC,"unknown state %08x", (*it)->type & (*it)->state);
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *        Transformation from tree to binary representation of jsonb        *
+ ****************************************************************************/
+typedef struct CompressState
+{
+	char	*begin;
+	char	*ptr;
+
+	struct {
+		uint32	i;
+		uint32	*header;
+		JEntry	*array;
+		char	*begin;
+	} *levelstate, *lptr, *pptr;
+
+	uint32	maxlevel;
+	
+} CompressState;
+
+#define	curLevelState	state->lptr
+#define prevLevelState	state->pptr
+
+static void
+putJEntryString(CompressState *state, JsonbValue* value, uint32 level, uint32 i)
+{
+	curLevelState = state->levelstate + level;
+
+	if (i == 0)
+		curLevelState->array[0].entry = JENTRY_ISFIRST;
+	else
+		curLevelState->array[i].entry = 0;
+
+	switch(value->type)
+	{
+		case jbvNull:
+			curLevelState->array[i].entry |= JENTRY_ISNULL;
+
+			if (i>0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvString:
+			memcpy(state->ptr, value->string.val, value->string.len);
+			state->ptr += value->string.len;
+
+			if (i == 0)
+				curLevelState->array[i].entry |= value->string.len;
+			else
+				curLevelState->array[i].entry |= 
+					(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+					value->string.len;
+			break;
+		case jbvBool:
+			curLevelState->array[i].entry |= (value->boolean) ?
+				JENTRY_ISTRUE : JENTRY_ISFALSE;
+
+			if (i>0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvNumeric:
+			{
+				int addlen = INTALIGN(state->ptr - state->begin) -
+								(state->ptr - state->begin);
+				int	numlen = VARSIZE_ANY(value->numeric); 
+
+				switch(addlen)
+				{
+					case 3:
+						*state->ptr = '\0'; state->ptr++;
+					case 2:
+						*state->ptr = '\0'; state->ptr++;
+					case 1:
+						*state->ptr = '\0'; state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->numeric, numlen);
+				state->ptr += numlen;
+
+				curLevelState->array[i].entry |= JENTRY_ISNUMERIC;
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + numlen;
+				else
+					curLevelState->array[i].entry |= 
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + numlen;
+				break;
+			}
+		case jbvBinary:
+			{
+				int addlen = INTALIGN(state->ptr - state->begin) -
+								(state->ptr - state->begin);
+
+				switch(addlen)
+				{
+					case 3:
+						*state->ptr = '\0'; state->ptr++;
+					case 2:
+						*state->ptr = '\0'; state->ptr++;
+					case 1:
+						*state->ptr = '\0'; state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->binary.data, value->binary.len);
+				state->ptr += value->binary.len;
+
+				curLevelState->array[i].entry |= JENTRY_ISNEST;
+
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + value->binary.len;
+				else
+					curLevelState->array[i].entry |=
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + value->binary.len;
+			}
+			break;
+		default:
+			elog(PANIC,"Unsupported JsonbValue type: %d", value->type);
+	}
+}
+
+static void
+compressCallback(void *arg, JsonbValue* value, uint32 flags, uint32 level)
+{
+	CompressState	*state = arg;
+
+	if (level == state->maxlevel) {
+		state->maxlevel *= 2;
+		state->levelstate = repalloc(state->levelstate,
+								 sizeof(*state->levelstate) * state->maxlevel);
+	}
+
+	curLevelState = state->levelstate + level;
+
+	if (flags & (WJB_BEGIN_ARRAY | WJB_BEGIN_OBJECT))
+	{
+		Assert(((flags & WJB_BEGIN_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_BEGIN_OBJECT) && value->type == jbvHash));
+
+		curLevelState->begin = state->ptr;
+
+		switch(INTALIGN(state->ptr - state->begin) -
+			   (state->ptr - state->begin))
+		{
+			case 3:
+				*state->ptr = '\0'; state->ptr++;
+			case 2:
+				*state->ptr = '\0'; state->ptr++;
+			case 1:
+				*state->ptr = '\0'; state->ptr++;
+			case 0:
+			default:
+				break;
+		}
+
+		curLevelState->header = (uint32*)state->ptr;
+		state->ptr += sizeof(*curLevelState->header);
+
+		curLevelState->array = (JEntry*)state->ptr;
+		curLevelState->i = 0;
+
+		if (value->type == jbvArray)
+		{
+			*curLevelState->header = value->array.nelems | JB_FLAG_ARRAY ;
+			state->ptr += sizeof(JEntry) * value->array.nelems;
+
+			if (value->array.scalar)
+			{
+				Assert(value->array.nelems == 1);
+				Assert(level == 0);
+				*curLevelState->header |= JB_FLAG_SCALAR;
+			}
+		}
+		else
+		{
+			*curLevelState->header = value->hash.npairs | JB_FLAG_OBJECT ;
+			state->ptr += sizeof(JEntry) * value->hash.npairs * 2;
+		}
+	}
+	else if (flags & WJB_ELEM)
+	{
+		putJEntryString(state, value, level, curLevelState->i);
+		curLevelState->i++;
+	}
+	else if (flags & WJB_KEY)
+	{
+		Assert(value->type == jbvString);
+
+		putJEntryString(state, value, level, curLevelState->i * 2);
+	}
+	else if (flags & WJB_VALUE)
+	{
+		putJEntryString(state, value, level, curLevelState->i * 2 + 1);
+		curLevelState->i++;
+	}
+	else if (flags & (WJB_END_ARRAY | WJB_END_OBJECT))
+	{
+		uint32	len, i;
+
+		Assert(((flags & WJB_END_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_END_OBJECT) && value->type == jbvHash));
+		if (level == 0)
+			return;
+
+		len = state->ptr - (char*)curLevelState->begin;
+
+		prevLevelState = curLevelState - 1;
+
+		if (*prevLevelState->header & JB_FLAG_ARRAY) {
+			i = prevLevelState->i;
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			if (i == 0)
+				prevLevelState->array[0].entry |= JENTRY_ISFIRST | len;
+			else
+				prevLevelState->array[i].entry |=
+					(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else if (*prevLevelState->header & JB_FLAG_OBJECT)
+		{
+			i = 2 * prevLevelState->i + 1; /* VALUE, not a KEY */
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			prevLevelState->array[i].entry |=
+				(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else
+		{
+			elog(PANIC, "Wrong parent");
+		}
+
+		Assert(state->ptr - curLevelState->begin <= value->size);
+		prevLevelState->i++;
+	}
+	else
+	{
+		elog(PANIC, "Wrong flags");
+	}
+}
+
+uint32
+compressJsonb(JsonbValue *v, char *buffer) {
+	uint32			l = 0;
+	CompressState	state;
+
+	state.begin = state.ptr = buffer;
+	state.maxlevel = 8;
+	state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel);
+
+	walkUncompressedJsonb(v, compressCallback, &state);
+
+	l = state.ptr - buffer;
+	Assert(l <= v->size);
+
+	return l;
+}
+
+/****************************************************************************
+ *                  Iteration-like forming jsonb                            *
+ *       Note: it believes by default in already sorted keys in hash,       *
+ *     although with r == WJB_END_OBJECT and v == NULL  it will sort itself *
+ ****************************************************************************/
+static ToJsonbState*
+pushState(ToJsonbState **state)
+{
+	ToJsonbState	*ns = palloc(sizeof(*ns));
+
+	ns->next = *state;
+	return ns;
+}
+
+static void
+appendArray(ToJsonbState *state, JsonbValue *v)
+{
+	JsonbValue	*a = &state->v;
+
+	Assert(a->type == jbvArray);
+
+	if (a->array.nelems >= state->size)
+	{
+		state->size *= 2;
+		a->array.elems = repalloc(a->array.elems,
+								   sizeof(*a->array.elems) * state->size);
+	}
+
+	a->array.elems[a->array.nelems ++] = *v;
+
+	a->size += v->size;
+}
+
+static void
+appendKey(ToJsonbState *state, JsonbValue *v)
+{
+	JsonbValue	*h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	if (h->hash.npairs >= state->size)
+	{
+		state->size *= 2;
+		h->hash.pairs = repalloc(h->hash.pairs,
+									sizeof(*h->hash.pairs) * state->size);
+	}
+
+	h->hash.pairs[h->hash.npairs].key = *v;
+	h->hash.pairs[h->hash.npairs].order = h->hash.npairs;
+
+	h->size += v->size;
+}
+
+static void
+appendValue(ToJsonbState *state, JsonbValue *v)
+{
+
+	JsonbValue	*h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	h->hash.pairs[h->hash.npairs++].value = *v;
+
+	h->size += v->size;
+}
+
+
+JsonbValue*
+pushJsonbValue(ToJsonbState **state, int r /* WJB_* */, JsonbValue *v) {
+	JsonbValue	*h = NULL;
+
+	switch(r)
+	{
+		case WJB_BEGIN_ARRAY:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvArray;
+			(*state)->v.size = 3*sizeof(JEntry);
+			(*state)->v.array.nelems = 0;
+			(*state)->v.array.scalar = (v && v->array.scalar) ? true : false;
+			(*state)->size = (v && v->type == jbvArray && v->array.nelems > 0)
+								? v->array.nelems : 4;
+			(*state)->v.array.elems = palloc(sizeof(*(*state)->v.array.elems) *
+											 (*state)->size);
+			break;
+		case WJB_BEGIN_OBJECT:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvHash;
+			(*state)->v.size = 3*sizeof(JEntry);
+			(*state)->v.hash.npairs = 0;
+			(*state)->size = (v && v->type == jbvHash && v->hash.npairs > 0) ?
+									v->hash.npairs : 4;
+			(*state)->v.hash.pairs = palloc(sizeof(*(*state)->v.hash.pairs) *
+											(*state)->size);
+			break;
+		case WJB_ELEM:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric || 
+				   v->type == jbvBinary); 
+			appendArray(*state, v);
+			break;
+		case WJB_KEY:
+			Assert(v->type == jbvString);
+			appendKey(*state, v);
+			break;
+		case WJB_VALUE:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric || 
+				   v->type == jbvBinary); 
+			appendValue(*state, v);
+			break;
+		case WJB_END_OBJECT:
+			h = &(*state)->v;
+			if (v == NULL)
+				uniqueJsonbValue(h);
+		case WJB_END_ARRAY:
+			h = &(*state)->v;
+			*state = (*state)->next;
+			if (*state)
+			{
+				switch((*state)->v.type)
+				{
+					case jbvArray:
+						appendArray(*state, h);
+						break;
+					case jbvHash:
+						appendValue(*state, h);
+						break;
+					default:
+						elog(PANIC, "wrong parent type: %d", (*state)->v.type);
+				}
+			}
+			break;
+		default:
+			elog(PANIC, "wrong type: %08x", r);
+	}
+
+	return h;
+}
+
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 90fa447..4e8b639 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -27,6 +27,7 @@
 #include "utils/builtins.h"
 #include "utils/hsearch.h"
 #include "utils/json.h"
+#include "utils/jsonb.h"
 #include "utils/jsonapi.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -63,6 +64,7 @@ static inline Datum each_worker(PG_FUNCTION_ARGS, bool as_text);
 /* semantic action functions for json_each */
 static void each_object_field_start(void *state, char *fname, bool isnull);
 static void each_object_field_end(void *state, char *fname, bool isnull);
+static void each_object_field_end_jsonb(void *state, char *fname, bool isnull);
 static void each_array_start(void *state);
 static void each_scalar(void *state, char *token, JsonTokenType tokentype);
 
@@ -70,6 +72,7 @@ static void each_scalar(void *state, char *token, JsonTokenType tokentype);
 static void elements_object_start(void *state);
 static void elements_array_element_start(void *state, bool isnull);
 static void elements_array_element_end(void *state, bool isnull);
+static void elements_array_element_end_jsonb(void *state, bool isnull);
 static void elements_scalar(void *state, char *token, JsonTokenType tokentype);
 
 /* turn a json object into a hash table */
@@ -218,12 +221,86 @@ typedef struct PopulateRecordsetState
  *
  * This SRF operates in value-per-call mode. It processes the
  * object during the first call, and the keys are simply stashed
- * in an array, whise size is expanded as necessary. This is probably
+ * in an array, whose size is expanded as necessary. This is probably
  * safe enough for a list of keys of a single object, since they are
  * limited in size to NAMEDATALEN and the number of keys is unlikely to
  * be so huge that it has major memory implications.
  */
 
+Datum
+jsonb_object_keys(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	OkeysState *state;
+	int			i;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext    oldcontext;
+		Jsonb           *jb = PG_GETARG_JSONB(0);
+		bool             skipNested = false;
+		JsonbIterator   *it;
+		JsonbValue	     v;
+		int              r = 0;
+
+	 
+		if (JB_ROOT_IS_SCALAR(jb))
+			elog(ERROR,"Cannot call jsonb_object_keys on a scalar");
+		else if (JB_ROOT_IS_ARRAY(jb))
+			elog(ERROR,"Cannot call jsonb_object_keys on an array");
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		state = palloc(sizeof(OkeysState));
+
+		state->result_size = JB_ROOT_COUNT(jb);
+		state->result_count = 0;
+		state->sent_count = 0;
+		state->result = palloc(state->result_size * sizeof(char *));
+
+		it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+		while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+		{
+			skipNested = true;
+			
+			if (r == WJB_KEY)
+			{
+				char * cstr;
+
+				cstr = palloc(v.string.len+1 * sizeof(char));
+				memcpy(cstr,v.string.val, v.string.len);
+				cstr[v.string.len] = '\0';
+				state->result[state->result_count++] = cstr;
+			}
+		}
+
+
+		MemoryContextSwitchTo(oldcontext);
+		funcctx->user_fctx = (void *) state;
+
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	state = (OkeysState *) funcctx->user_fctx;
+
+	if (state->sent_count < state->result_count)
+	{
+		char	   *nxt = state->result[state->sent_count++];
+
+		SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
+	}
+
+	/* cleanup to reduce or eliminate memory leaks */
+	for (i = 0; i < state->result_count; i++)
+		pfree(state->result[i]);
+	pfree(state->result);
+	pfree(state);
+
+	SRF_RETURN_DONE(funcctx);
+}
+
 
 Datum
 json_object_keys(PG_FUNCTION_ARGS)
@@ -234,12 +311,26 @@ json_object_keys(PG_FUNCTION_ARGS)
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		text	   *json = PG_GETARG_TEXT_P(0);
-		JsonLexContext *lex = makeJsonLexContext(json, true);
+		text	   *json; //  = PG_GETARG_TEXT_P(0);
+		JsonLexContext *lex; //  = makeJsonLexContext(json, true);
 		JsonSemAction *sem;
 
 		MemoryContext oldcontext;
 
+		if (get_fn_expr_argtype(fcinfo->flinfo, 0) == JSONOID)
+		{
+			/* just get the text */
+			json = PG_GETARG_TEXT_P(0);
+		}
+		else
+		{
+			Jsonb      *jb = PG_GETARG_JSONB(0);
+			
+			json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+		}
+
+		lex = makeJsonLexContext(json, true);
+
 		funcctx = SRF_FIRSTCALL_INIT();
 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
@@ -336,9 +427,9 @@ okeys_scalar(void *state, char *token, JsonTokenType tokentype)
 }
 
 /*
- * json getter functions
+ * json and jsonb getter functions
  * these implement the -> ->> #> and #>> operators
- * and the json_extract_path*(json, text, ...) functions
+ * and the json{b?}_extract_path*(json, text, ...) functions
  */
 
 
@@ -359,6 +450,26 @@ json_object_field(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	text       *jsontext;
+	text	   *result;
+	text	   *fname = PG_GETARG_TEXT_P(1);
+	char	   *fnamestr = text_to_cstring(fname);
+
+	jsontext = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	
+	result = get_worker(jsontext, fnamestr, -1, NULL, NULL, -1, false);
+
+	if (result != NULL)
+		PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	else
+		PG_RETURN_NULL();
+
+}
+
+Datum
 json_object_field_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -375,6 +486,26 @@ json_object_field_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field_text(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	text       *jsontext;
+	text	   *result;
+	text	   *fname = PG_GETARG_TEXT_P(1);
+	char	   *fnamestr = text_to_cstring(fname);
+
+	jsontext = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+
+	result = get_worker(jsontext, fnamestr, -1, NULL, NULL, -1, true);
+	
+	if (result != NULL)
+		PG_RETURN_TEXT_P(result);
+	else
+		PG_RETURN_NULL();
+
+}
+
+Datum
 json_array_element(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -390,6 +521,46 @@ json_array_element(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	int			element = PG_GETARG_INT32(1);
+	JsonbIterator   *it;
+	JsonbValue       v;
+	int              r = 0;
+	bool             skipNested = false;
+	int              element_number = 0;
+
+    
+   if (JB_ROOT_IS_SCALAR(jb))
+       elog(ERROR,"Cannot call jsonb_array_element on a scalar");
+   else if (JB_ROOT_IS_OBJECT(jb))
+       elog(ERROR,"Cannot call jsonb_array_element on an object");
+
+   Assert(JB_ROOT_IS_ARRAY(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+		
+		// elog(NOTICE, "r = %d",r);
+		
+		if (r == WJB_ELEM)
+		{
+			
+			if (element_number++ == element)
+			{
+				PG_RETURN_JSONB(JsonbValueToJsonb(&v));
+			}
+		}
+	}
+
+	PG_RETURN_NULL(); 
+}
+
+Datum
 json_array_element_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -405,24 +576,96 @@ json_array_element_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element_text(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	int			element = PG_GETARG_INT32(1);
+	JsonbIterator   *it;
+	JsonbValue       v;
+	int              r = 0;
+	bool             skipNested = false;
+	int              element_number = 0;
+
+    
+   if (JB_ROOT_IS_SCALAR(jb))
+       elog(ERROR,"Cannot call jsonb_array_element_text on a scalar");
+   else if (JB_ROOT_IS_OBJECT(jb))
+       elog(ERROR,"Cannot call jsonb_array_element_text on an object");
+
+   Assert(JB_ROOT_IS_ARRAY(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+		
+		// elog(NOTICE, "r = %d",r);
+		
+		if (r == WJB_ELEM)
+		{
+			
+			if (element_number++ == element)
+			{
+				/* 
+				 * if it's a scalar string it needs to be de-escaped, 
+				 * otherwise just return the text 
+				 */
+				text *result;
+				if (v.type == jbvString)
+				{
+					result = cstring_to_text_with_len(v.string.val, v.string.len);
+				}
+				else if (v.type == jbvNull)
+				{
+					PG_RETURN_NULL(); 
+				}
+				else
+				{
+					StringInfo jtext = makeStringInfo();
+					(void) JsonbToCString(jtext, JsonbValueToJsonb(&v), -1);
+					result = cstring_to_text_with_len(jtext->data, jtext->len);
+				}
+				PG_RETURN_TEXT_P(result);
+			}
+		}
+	}
+
+	PG_RETURN_NULL(); 
+}
+
+Datum
 json_extract_path(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, false);
 }
 
 Datum
+jsonb_extract_path(PG_FUNCTION_ARGS)
+{
+	return get_path_all(fcinfo, false);
+}
+
+Datum
 json_extract_path_text(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, true);
 }
 
+Datum
+jsonb_extract_path_text(PG_FUNCTION_ARGS)
+{
+	return get_path_all(fcinfo, true);
+}
+
 /*
  * common routine for extract_path functions
  */
 static inline Datum
 get_path_all(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
 	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
 	text	   *result;
 	Datum	   *pathtext;
@@ -434,6 +677,19 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	long		ind;
 	char	   *endptr;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
 	if (array_contains_nulls(path))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -472,9 +728,17 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	result = get_worker(json, NULL, -1, tpath, ipath, npath, as_text);
 
 	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
+	{
+		if (val_type == JSONOID || as_text)
+			PG_RETURN_TEXT_P(result);
+		else
+			PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	}
 	else
+	{
+		/* null is null regardless */
 		PG_RETURN_NULL();
+	}
 }
 
 /*
@@ -814,12 +1078,14 @@ get_scalar(void *state, char *token, JsonTokenType tokentype)
 Datum
 json_array_length(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
 	AlenState  *state;
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(AlenState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -839,6 +1105,24 @@ json_array_length(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(state->count);
 }
 
+Datum
+jsonb_array_length(PG_FUNCTION_ARGS)
+{
+	Jsonb *jb = PG_GETARG_JSONB(0);
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a scalar")));
+	else if (! JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a non-array")));
+
+	PG_RETURN_INT32(JB_ROOT_COUNT(jb));
+	
+}
+
 /*
  * These next two check ensure that the json is an array (since it can't be
  * a scalar or an object).
@@ -895,22 +1179,49 @@ json_each(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_each(PG_FUNCTION_ARGS)
+{
+	return each_worker(fcinfo, false);
+}
+
+Datum
 json_each_text(PG_FUNCTION_ARGS)
 {
 	return each_worker(fcinfo, true);
 }
 
+Datum
+jsonb_each_text(PG_FUNCTION_ARGS)
+{
+	return each_worker(fcinfo, true);
+}
+
 static inline Datum
 each_worker(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	JsonLexContext *lex = makeJsonLexContext(json, true);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	TupleDesc	tupdesc;
 	EachState  *state;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
+	lex = makeJsonLexContext(json, true);
 	state = palloc0(sizeof(EachState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -944,7 +1255,10 @@ each_worker(PG_FUNCTION_ARGS, bool as_text)
 	sem->array_start = each_array_start;
 	sem->scalar = each_scalar;
 	sem->object_field_start = each_object_field_start;
-	sem->object_field_end = each_object_field_end;
+	if (val_type == JSONOID)
+		sem->object_field_end = each_object_field_end;
+	else
+		sem->object_field_end = each_object_field_end_jsonb;
 
 	state->normalize_results = as_text;
 	state->next_scalar = false;
@@ -1033,6 +1347,61 @@ each_object_field_end(void *state, char *fname, bool isnull)
 }
 
 static void
+each_object_field_end_jsonb(void *state, char *fname, bool isnull)
+{
+	EachState  *_state = (EachState *) state;
+	MemoryContext old_cxt;
+	int			len;
+	Jsonb	   *val;
+	HeapTuple	tuple;
+	Datum		values[2];
+	bool		nulls[2] = {false, false};
+
+	/* skip over nested objects */
+	if (_state->lex->lex_level != 1)
+		return;
+
+	/* use the tmp context so we can clean up after each tuple is done */
+	old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
+
+	values[0] = CStringGetTextDatum(fname);
+
+	if (isnull && _state->normalize_results)
+	{
+		nulls[1] = true;
+		values[1] = (Datum) NULL;
+	}
+	else if (_state->next_scalar)
+	{
+		values[1] = CStringGetTextDatum(_state->normalized_scalar);
+		_state->next_scalar = false;
+	}
+	else if (_state->normalize_results)
+	{
+		len = _state->lex->prev_token_terminator - _state->result_start;
+		values[1] = PointerGetDatum(cstring_to_text_with_len(_state->result_start, len));
+	}
+	else
+	{
+		char *cstr;
+		len = _state->lex->prev_token_terminator - _state->result_start;
+		cstr = palloc(len+1 * sizeof(char));
+		memcpy(cstr,_state->result_start,len);
+		cstr[len] = '\0';
+		val = (Jsonb*)DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(cstr)));
+		values[1] = PointerGetDatum(val);
+	}
+
+	tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
+
+	tuplestore_puttuple(_state->tuple_store, tuple);
+
+	/* clean up and switch back */
+	MemoryContextSwitchTo(old_cxt);
+	MemoryContextReset(_state->tmp_cxt);
+}
+
+static void
 each_array_start(void *state)
 {
 	EachState  *_state = (EachState *) state;
@@ -1067,19 +1436,41 @@ each_scalar(void *state, char *token, JsonTokenType tokentype)
  *
  * a lot of this processing is similar to the json_each* functions
  */
+
+Datum
+jsonb_array_elements(PG_FUNCTION_ARGS)
+{
+	return json_array_elements(fcinfo);
+}
+
 Datum
 json_array_elements(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
 
-	/* elements doesn't need any escaped strings, so use false here */
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	TupleDesc	tupdesc;
 	ElementsState *state;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
+	/* elements doesn't need any escaped strings, so use false here */
+	lex  = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(ElementsState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -1114,7 +1505,10 @@ json_array_elements(PG_FUNCTION_ARGS)
 	sem->object_start = elements_object_start;
 	sem->scalar = elements_scalar;
 	sem->array_element_start = elements_array_element_start;
-	sem->array_element_end = elements_array_element_end;
+	if (val_type == JSONOID)
+		sem->array_element_end = elements_array_element_end;
+	else
+		sem->array_element_end = elements_array_element_end_jsonb;
 
 	state->lex = lex;
 	state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
@@ -1174,6 +1568,41 @@ elements_array_element_end(void *state, bool isnull)
 }
 
 static void
+elements_array_element_end_jsonb(void *state, bool isnull)
+{
+	ElementsState *_state = (ElementsState *) state;
+	MemoryContext old_cxt;
+	int			len;
+	Jsonb      *jbval;
+	HeapTuple	tuple;
+	Datum		values[1];
+	static bool nulls[1] = {false};
+	char *cstr;
+
+	/* skip over nested objects */
+	if (_state->lex->lex_level != 1)
+		return;
+
+	/* use the tmp context so we can clean up after each tuple is done */
+	old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
+
+	len = _state->lex->prev_token_terminator - _state->result_start;
+	cstr = palloc(len+1 * sizeof(char));
+	memcpy(cstr,_state->result_start,len);
+	cstr[len] = '\0';
+	jbval = (Jsonb*)DatumGetPointer(DirectFunctionCall1(jsonb_in, CStringGetDatum(cstr)));
+	values[0] = PointerGetDatum(jbval);
+
+	tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
+
+	tuplestore_puttuple(_state->tuple_store, tuple);
+
+	/* clean up and switch back */
+	MemoryContextSwitchTo(old_cxt);
+	MemoryContextReset(_state->tmp_cxt);
+}
+
+static void
 elements_object_start(void *state)
 {
 	ElementsState *_state = (ElementsState *) state;
@@ -1214,9 +1643,16 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
  * field in the record is then looked up by name.
  */
 Datum
+jsonb_populate_record(PG_FUNCTION_ARGS)
+{
+	return json_populate_record(fcinfo);
+}
+
+Datum
 json_populate_record(PG_FUNCTION_ARGS)
 {
 	Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	Oid         jtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
 	text	   *json;
 	bool		use_json_as_text;
 	HTAB	   *json_hash;
@@ -1234,6 +1670,8 @@ json_populate_record(PG_FUNCTION_ARGS)
 	char		fname[NAMEDATALEN];
 	JsonHashEntry *hashentry;
 
+	Assert(jtype == JSONOID || jtype == JSONBOID);
+
 	use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
 
 	if (!type_is_rowtype(argtype))
@@ -1268,7 +1706,17 @@ json_populate_record(PG_FUNCTION_ARGS)
 		tupTypmod = HeapTupleHeaderGetTypMod(rec);
 	}
 
-	json = PG_GETARG_TEXT_P(1);
+	if (jtype == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(1);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(1);
+		
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
 
 	json_hash = get_json_object_as_hash(json, "json_populate_record", use_json_as_text);
 
@@ -1559,9 +2007,17 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
  * per object in the array.
  */
 Datum
+jsonb_populate_recordset(PG_FUNCTION_ARGS)
+{
+	return json_populate_recordset(fcinfo);
+}
+
+
+Datum
 json_populate_recordset(PG_FUNCTION_ARGS)
 {
 	Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	Oid         jtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
 	text	   *json;
 	bool		use_json_as_text;
 	ReturnSetInfo *rsi;
@@ -1621,7 +2077,17 @@ json_populate_recordset(PG_FUNCTION_ARGS)
 	if (PG_ARGISNULL(1))
 		PG_RETURN_NULL();
 
-	json = PG_GETARG_TEXT_P(1);
+	if (jtype == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(1);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(1);
+		
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
 
 	if (PG_ARGISNULL(0))
 		rec = NULL;
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 6aa4890..143a451 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1753,6 +1753,18 @@ DATA(insert OID = 3966 (  "#>"	   PGNSP PGUID b f f 114 1009 114 0 0 json_extrac
 DESCR("get value from json with path elements");
 DATA(insert OID = 3967 (  "#>>"    PGNSP PGUID b f f 114 1009 25 0 0 json_extract_path_text_op - - ));
 DESCR("get value from json as text with path elements");
+DATA(insert OID = 3211 (  "->"	   PGNSP PGUID b f f 3802 25 3802 0 0 jsonb_object_field - - ));
+DESCR("get jsonb object field");
+DATA(insert OID = 3204 (  "->>"    PGNSP PGUID b f f 3802 25 25 0 0 jsonb_object_field_text - - ));
+DESCR("get jsonb object field as text");
+DATA(insert OID = 3212 (  "->"	   PGNSP PGUID b f f 3802 23 3802 0 0 jsonb_array_element - - ));
+DESCR("get jsonb array element");
+DATA(insert OID = 3205 (  "->>"    PGNSP PGUID b f f 3802 23 25 0 0 jsonb_array_element_text - - ));
+DESCR("get jsonb array element as text");
+DATA(insert OID = 3213 (  "#>"	   PGNSP PGUID b f f 3802 1009 3802 0 0 jsonb_extract_path_op - - ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3206 (  "#>>"    PGNSP PGUID b f f 3802 1009 25 0 0 jsonb_extract_path_text_op - - ));
+DESCR("get value from jsonb as text with path elements");
 
 
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ab05c46..bf776d1 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4457,6 +4457,44 @@ DESCR("I/O");
 DATA(insert OID = 3774 (  regdictionarysend PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3769" _null_ _null_ _null_ _null_ regdictionarysend _null_ _null_ _null_ ));
 DESCR("I/O");
 
+/* jsonb */
+DATA(insert OID =  3806 (  jsonb_in			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2275" _null_ _null_ _null_ _null_ jsonb_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3805 (  jsonb_recv		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2281" _null_ _null_ _null_ _null_ jsonb_recv _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3804 (  jsonb_out		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3802" _null_ _null_ _null_ _null_ jsonb_out _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3803 (  jsonb_send		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3802" _null_ _null_ _null_ _null_	jsonb_send _null_ _null_ _null_ ));
+DESCR("I/O");
+
+DATA(insert OID = 3969 (  jsonb_object_field			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field _null_ _null_ _null_ ));
+DATA(insert OID = 3179 (  jsonb_object_field_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field_text _null_ _null_ _null_ ));
+DATA(insert OID = 3180 (  jsonb_array_element		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element _null_ _null_ _null_ ));
+DATA(insert OID = 3195 (  jsonb_array_element_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element_text _null_ _null_ _null_ ));
+DATA(insert OID = 3196 (  jsonb_extract_path			PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 3802 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3199 (  jsonb_extract_path_op		PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DATA(insert OID = 3200 (  jsonb_extract_path_text	PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 25 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DESCR("get value from jsonb as text with path elements");
+DATA(insert OID = 3197 (  jsonb_extract_path_text_op PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 25 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DATA(insert OID = 3198 (  jsonb_array_elements		PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 3802 "3802" "{3802,3802}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3207 (  jsonb_array_length			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "3802" _null_ _null_ _null_ _null_ jsonb_array_length _null_ _null_ _null_ ));
+DESCR("length of jsonb array");
+DATA(insert OID = 3201 (  jsonb_object_keys			PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_object_keys _null_ _null_ _null_ ));
+DESCR("get jsonb object keys");
+DATA(insert OID = 3208 (  jsonb_each				   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,3802}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3202 (  jsonb_each_text		   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,25}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each_text _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3209 (  jsonb_populate_record	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_record _null_ _null_ _null_ ));
+DESCR("get record fields from a jsonb object");
+DATA(insert OID = 3203 (  jsonb_populate_recordset  PGNSP PGUID 12 1 100 0 0 f f f f f t s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_recordset _null_ _null_ _null_ ));
+DESCR("get set of records with fields from a jsonb array of objects");
+DATA(insert OID = 3210 (  jsonb_typeof              PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_typeof _null_ _null_ _null_ ));
+DESCR("get the type of a jsonb value");
+
+
 /* txid */
 DATA(insert OID = 2939 (  txid_snapshot_in			PGNSP PGUID 12 1  0 0 0 f f f f t f i 1 0 2970 "2275" _null_ _null_ _null_ _null_ txid_snapshot_in _null_ _null_ _null_ ));
 DESCR("I/O");
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 3fc20c6..7fb8999 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -600,6 +600,12 @@ DATA(insert OID = 3645 ( _tsquery		PGNSP PGUID -1 f b A f t \054 0 3615 0 array_
 DATA(insert OID = 3735 ( _regconfig		PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 
+/* jsonb */
+DATA(insert OID = 3802 ( jsonb			PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DESCR("Binary JSON");
+#define JSONBOID 3802
+DATA(insert OID = 3807 ( _jsonb			PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 DATA(insert OID = 2970 ( txid_snapshot	PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 DESCR("txid snapshot");
 DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 9982e59..3610fc8 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -293,6 +293,15 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
 		PG_RETURN_DATUM(_result); \
 	} while (0)
 
+#define SRF_RETURN_NEXT_NULL(_funcctx) \
+	do { \
+		ReturnSetInfo	   *rsi; \
+		(_funcctx)->call_cntr++; \
+		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+		rsi->isDone = ExprMultipleResult; \
+		PG_RETURN_NULL(); \
+	} while (0)
+
 #define  SRF_RETURN_DONE(_funcctx) \
 	do { \
 		ReturnSetInfo	   *rsi; \
diff --git a/src/include/utils/json.h b/src/include/utils/json.h
index 25bfafb..c2fc7ee 100644
--- a/src/include/utils/json.h
+++ b/src/include/utils/json.h
@@ -50,4 +50,18 @@ extern Datum json_array_elements(PG_FUNCTION_ARGS);
 extern Datum json_populate_record(PG_FUNCTION_ARGS);
 extern Datum json_populate_recordset(PG_FUNCTION_ARGS);
 
+extern Datum jsonb_object_field(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_field_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_keys(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_length(PG_FUNCTION_ARGS);
+extern Datum jsonb_each(PG_FUNCTION_ARGS);
+extern Datum jsonb_each_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_elements(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_record(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_recordset(PG_FUNCTION_ARGS);
+
 #endif   /* JSON_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
new file mode 100644
index 0000000..15e2927
--- /dev/null
+++ b/src/include/utils/jsonb.h
@@ -0,0 +1,230 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.h
+ *    Declarations for JSONB data type support.
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/include/utils/jsonb.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef __JSONB_H__
+#define __JSONB_H__
+
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "utils/array.h"
+#include "utils/numeric.h"
+
+/*
+ * JEntry: there is one of these for each key _and_ value in an jsonb
+ *
+ * the position offset points to the _end_ so that we can get the length
+ * by subtraction from the previous entry.	the ISFIRST flag lets us tell
+ * whether there is a previous entry.
+ */
+typedef struct
+{
+	uint32		entry;
+} JEntry;
+
+#define JENTRY_ISFIRST		0x80000000
+#define JENTRY_ISSTRING 	(0x00000000) /* keep binary compatibility */
+#define JENTRY_ISNUMERIC	(0x10000000)
+#define JENTRY_ISNEST		(0x20000000)
+#define JENTRY_ISNULL		(0x40000000) /* keep binary compatibility */
+#define JENTRY_ISBOOL		(0x10000000 | 0x20000000)
+#define JENTRY_ISFALSE		JENTRY_ISBOOL
+#define JENTRY_ISTRUE		(0x10000000 | 0x20000000 | 0x40000000)
+
+/* JENTRY_ISOBJECT, JENTRY_ISARRAY and JENTRY_ISCALAR is only used in send/recv */
+#define JENTRY_ISOBJECT		(0x20000000)
+#define JENTRY_ISARRAY		(0x20000000 | 0x40000000)
+#define JENTRY_ISCALAR		(0x10000000 | 0x40000000)
+
+#define JENTRY_POSMASK 	0x0FFFFFFF
+#define JENTRY_TYPEMASK	(~(JENTRY_POSMASK | JENTRY_ISFIRST))
+
+/* note possible multiple evaluations, also access to prior array element */
+#define JBE_ISFIRST(he_) 		(((he_).entry & JENTRY_ISFIRST) != 0)
+#define JBE_ISSTRING(he_)		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
+#define JBE_ISNUMERIC(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
+#define JBE_ISNEST(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNEST)
+#define JBE_ISNULL(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNULL)
+#define JBE_ISBOOL(he_) 		(((he_).entry & JENTRY_TYPEMASK & JENTRY_ISBOOL) == JENTRY_ISBOOL)
+#define JBE_ISBOOL_TRUE(he_) 	(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISTRUE)
+#define JBE_ISBOOL_FALSE(he_) 	(JBE_ISBOOL(he_) && !JBE_ISBOOL_TRUE(he_))
+
+#define JBE_ENDPOS(he_) ((he_).entry & JENTRY_POSMASK)
+#define JBE_OFF(he_) (JBE_ISFIRST(he_) ? 0 : JBE_ENDPOS((&(he_))[-1]))
+#define JBE_LEN(he_) (JBE_ISFIRST(he_)	\
+					  ? JBE_ENDPOS(he_) \
+					  : JBE_ENDPOS(he_) - JBE_ENDPOS((&(he_))[-1]))
+
+/*
+ * determined by the size of "endpos" (ie JENTRY_POSMASK)
+ */
+#define JSONB_MAX_STRING_LEN 		JENTRY_POSMASK
+
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	/* header of hash or array jsonb type */
+	/* array of JEntry follows */
+} Jsonb;
+
+/*
+ * it's not possible to get more than 2^28 items into an jsonb.
+ */
+#define JB_FLAG_UNUSED 			0x80000000
+#define JB_FLAG_ARRAY			0x40000000
+#define JB_FLAG_OBJECT			0x20000000
+#define JB_FLAG_SCALAR			0x10000000
+
+#define JB_COUNT_MASK			0x0FFFFFFF
+
+#define JB_ISEMPTY(jbp_)		(VARSIZE(jbp_) <= VARHDRSZ)
+#define JB_ROOT_COUNT(jbp_) 	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_COUNT_MASK))
+#define JB_ROOT_IS_OBJECT(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_OBJECT))
+#define JB_ROOT_IS_ARRAY(jbp_) 	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_ARRAY))
+#define JB_ROOT_IS_SCALAR(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_SCALAR))
+
+#define DatumGetJsonb(d) 	((Jsonb*) PG_DETOAST_DATUM(d))
+#define JsonbGetDatum(p)	PointerGetDatum(p)
+
+#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x)
+
+typedef struct JsonbPair JsonbPair;
+typedef struct JsonbValue JsonbValue;
+
+struct JsonbValue {
+	enum {
+		jbvNull,
+		jbvString,
+		jbvNumeric,
+		jbvBool,
+		jbvArray,
+		jbvHash,
+		jbvBinary  /* binary form of jbvArray/jbvHash */
+	} type;
+
+	uint32		size; /* estimation size of node (including subnodes) */
+
+	union {
+		Numeric			numeric;
+		bool			boolean;
+		struct {
+			uint32		len;
+			char 		*val; /* could be not null-terminated */
+		} string;
+
+		struct {
+			int			nelems;
+			JsonbValue	*elems;
+			bool		scalar; /* scalar actually shares representation with array */
+		} array;
+
+		struct {
+			int			npairs;
+			JsonbPair 	*pairs;
+		} hash;
+
+		struct {
+			uint32		len;
+			char		*data;
+		} binary;
+	};
+
+}; 
+
+struct JsonbPair {
+	JsonbValue	key;
+	JsonbValue	value;
+	uint32		order; /* to keep order of pairs with equal key */ 
+}; 
+
+/*
+ * jsonb support functios
+ */
+
+#define WJB_KEY         	(0x001)
+#define WJB_VALUE       	(0x002)
+#define WJB_ELEM       		(0x004)
+#define WJB_BEGIN_ARRAY 	(0x008)
+#define WJB_END_ARRAY   	(0x010)
+#define WJB_BEGIN_OBJECT    (0x020)
+#define WJB_END_OBJECT      (0x040)
+
+typedef void (*walk_jsonb_cb)(void* /*arg*/, JsonbValue* /* value */, 
+											uint32 /* flags */, uint32 /* level */);
+extern void walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg);
+
+extern int compareJsonbStringValue(const void *a, const void *b, void *arg);
+extern int compareJsonbPair(const void *a, const void *b, void *arg);
+
+extern int compareJsonbBinaryValue(char *a, char *b);
+extern int compareJsonbValue(JsonbValue *a, JsonbValue *b);
+
+extern JsonbValue* findUncompressedJsonbValueByValue(char *buffer, uint32 flags, 
+												uint32 *lowbound, JsonbValue* key);
+extern JsonbValue* findUncompressedJsonbValue(char *buffer, uint32 flags, 
+												uint32 *lowbound, char *key, uint32 keylen);
+
+extern JsonbValue* getJsonbValue(char *buffer, uint32 flags, int32 i);
+
+typedef struct ToJsonbState
+{
+	JsonbValue             v;
+	uint32                  size;
+	struct ToJsonbState    *next;
+} ToJsonbState;
+
+extern JsonbValue* pushJsonbValue(ToJsonbState **state, int r /* WJB_* */, JsonbValue *v);
+
+extern void uniqueJsonbValue(JsonbValue *v);
+
+extern uint32 compressJsonb(JsonbValue *v, char *buffer);
+
+typedef struct JsonbIterator
+{
+	uint32					type;
+	uint32					nelems;
+	JEntry					*array;
+	bool					isScalar;
+	char					*data;
+	char					*buffer; /* unparsed buffer */
+
+	int						i;
+
+	/*
+	 * enum members should be freely OR'ed with JB_FLAG_ARRAY/JB_FLAG_JSONB 
+	 * with possiblity of decoding. See optimization in JsonbIteratorGet()
+	 */
+	enum {
+		jbi_start 	= 0x00,
+		jbi_key		= 0x01,
+		jbi_value	= 0x02,
+		jbi_elem	= 0x04
+	} state;
+
+	struct JsonbIterator	*next;
+} JsonbIterator;
+
+extern 	JsonbIterator*	JsonbIteratorInit(char *buffer);
+extern	int /* WJB_* */	JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested);
+
+extern Datum jsonb_in(PG_FUNCTION_ARGS);
+extern Datum jsonb_out(PG_FUNCTION_ARGS);
+extern Datum jsonb_recv(PG_FUNCTION_ARGS);
+extern Datum jsonb_send(PG_FUNCTION_ARGS);
+
+extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
+
+extern char *JsonbToCString(StringInfo out, char *in, int estimated_len);
+extern Jsonb *JsonbValueToJsonb(JsonbValue *v);
+
+#endif   /* __JSONB_H__ */
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
new file mode 100644
index 0000000..12be1cc
--- /dev/null
+++ b/src/test/regress/expected/jsonb.out
@@ -0,0 +1,845 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ octet_length 
+--------------
+            5
+(1 row)
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+      array_to_json       
+--------------------------
+ [{"a": 1},{"b": [2, 3]}]
+(1 row)
+
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot extract element from a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot extract field from a non-object
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_array_element on a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  Cannot call jsonb_array_element on an object
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  Cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ correct_in_utf8 
+-----------------
+              10
+(1 row)
+
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+   correct_in_utf8    
+----------------------
+ the Copyright © sign
+(1 row)
+
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
new file mode 100644
index 0000000..cad1ef9
--- /dev/null
+++ b/src/test/regress/expected/jsonb_1.out
@@ -0,0 +1,845 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT octet_length('"\uaBcD"'::jsonb::text);
+                            ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: ...
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+      array_to_json       
+--------------------------
+ [{"a": 1},{"b": [2, 3]}]
+(1 row)
+
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  cannot extract element from a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  cannot extract field from a non-object
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_array_element on a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  Cannot call jsonb_array_element on an object
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  Cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc3...
+                                   ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a'...
+                     ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 5758b07..51238be 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -98,8 +98,7 @@ test: event_trigger
 # ----------
 # Another group of parallel tests
 # ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json indirect_toast
-
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast
 # ----------
 # Another group of parallel tests
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 78348f5..e414ec1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -121,6 +121,7 @@ test: xmlmap
 test: functional_deps
 test: advisory_lock
 test: json
+test: jsonb
 test: indirect_toast
 test: plancache
 test: limit
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
new file mode 100644
index 0000000..38959a8
--- /dev/null
+++ b/src/test/regress/sql/jsonb.sql
@@ -0,0 +1,265 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+SELECT '"abc"'::jsonb;			-- OK
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+SELECT '0'::jsonb;				-- OK
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+SELECT '0.1'::jsonb;				-- OK
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+SELECT '1e100'::jsonb;			-- OK
+SELECT '1.3e100'::jsonb;			-- OK
+SELECT '1f2'::jsonb;				-- ERROR
+SELECT '0.x1'::jsonb;			-- ERROR
+SELECT '1.3ex100'::jsonb;		-- ERROR
+
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+SELECT '[1,2]'::jsonb;			-- OK
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+SELECT '{"abc":1}'::jsonb;		-- OK
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+SELECT 'false'::jsonb;			-- OK
+SELECT 'null'::jsonb;			-- OK
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+SELECT ''::jsonb;				-- ERROR, no value
+SELECT '    '::jsonb;			-- ERROR, no value
+
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+
+
+-- jsonb extraction functions
+
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+
+-- nulls
+
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+
+
+-- array length
+
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+
+SELECT jsonb_array_length('[]');
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+
+SELECT jsonb_array_length('4');
+
+-- each
+
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+-- extract_path, extract_path_as_text
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+
+-- extract_path nulls
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+
+-- extract_path operators
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+
+-- array_elements
+
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+
+-- populate_recordset
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+
+-- using the default use_json_as_text argument
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+
+
+-- handling of unicode surrogate pairs
+
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+
+--handling of simple unicode escapes
+
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
#53Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#52)
1 attachment(s)
Re: nested hstore patch - FailedAssertion("!(value->array.nelems == 1)

On 01/15/2014 11:25 AM, Andrew Dunstan wrote:

But here is the latest patch from that.

And one more.

here is the current state of the jsonb part of this:

all but two sets of functions (jsonb_extract_path* and the associated
operators #> and #>>, and
jsonb_populate_record/jsonb_populate_record_set) have been reimplemented
using the faster processing of jsonb. Those two sets still fall back on
processing json text. These will be fixed very shortly. I'm waiting on
some promised documentation. There is also some code cleanup (add more
comments, use ereport() instead of elog() and the like) that needs to be
done. However, it all works, and is just pretty much completely
consistent with the way json works.

I'm sure Oleg, Teodor and I will be able to get this whole thing whipped
into shape pretty quickly.

cheers

andrew

Attachments:

nested_hstore-4.patchtext/x-patch; name=nested_hstore-4.patchDownload
diff --git a/contrib/hstore/.gitignore b/contrib/hstore/.gitignore
index 5dcb3ff..b84aac6 100644
--- a/contrib/hstore/.gitignore
+++ b/contrib/hstore/.gitignore
@@ -2,3 +2,6 @@
 /log/
 /results/
 /tmp_check/
+/hstore_gram.c
+/hstore_gram.h
+/hstore_scan.c
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 43b7e5f..fb9421f 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -2,13 +2,16 @@
 
 MODULE_big = hstore
 OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
-	crc32.o
+		hstore_gram.o
 
 EXTENSION = hstore
-DATA = hstore--1.2.sql hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
-	hstore--unpackaged--1.0.sql
+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
 
-REGRESS = hstore
+REGRESS = hstore nested types
+
+EXTRA_CLEAN = y.tab.c y.tab.h \
+				hstore_gram.c hstore_scan.c hstore_gram.h
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
@@ -20,3 +23,13 @@ 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
deleted file mode 100644
index c82fc66..0000000
--- a/contrib/hstore/crc32.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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 <srg@quick.com>
- */
-
-#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
deleted file mode 100644
index f5bfd82..0000000
--- a/contrib/hstore/crc32.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * 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/expected/hstore.out b/contrib/hstore/expected/hstore.out
index 2114143..c420c8b 100644
--- a/contrib/hstore/expected/hstore.out
+++ b/contrib/hstore/expected/hstore.out
@@ -199,6 +199,24 @@ select 'aa=>"NuLl"'::hstore;
  "aa"=>"NuLl"
 (1 row)
 
+select 'aa=>nul'::hstore;
+   hstore    
+-------------
+ "aa"=>"nul"
+(1 row)
+
+select 'aa=>NuL'::hstore;
+   hstore    
+-------------
+ "aa"=>"NuL"
+(1 row)
+
+select 'aa=>"NuL"'::hstore;
+   hstore    
+-------------
+ "aa"=>"NuL"
+(1 row)
+
 select e'\\=a=>q=w'::hstore;
    hstore    
 -------------
@@ -432,63 +450,63 @@ select hstore 'a=>NULL, b=>qq' ?& '{}'::text[];
 
 -- delete
 select delete('a=>1 , b=>2, c=>3'::hstore, 'a');
-       delete       
---------------------
- "b"=>"2", "c"=>"3"
+     delete     
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>null , b=>2, c=>3'::hstore, 'a');
-       delete       
---------------------
- "b"=>"2", "c"=>"3"
+     delete     
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'b');
-       delete       
---------------------
- "a"=>"1", "c"=>"3"
+     delete     
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'c');
-       delete       
---------------------
- "a"=>"1", "b"=>"2"
+     delete     
+----------------
+ "a"=>1, "b"=>2
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, 'd');
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'a'::text;
-      ?column?      
---------------------
- "b"=>"2", "c"=>"3"
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>null , b=>2, c=>3'::hstore - 'a'::text;
-      ?column?      
---------------------
- "b"=>"2", "c"=>"3"
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'b'::text;
-      ?column?      
---------------------
- "a"=>"1", "c"=>"3"
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'c'::text;
-      ?column?      
---------------------
- "a"=>"1", "b"=>"2"
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - 'd'::text;
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text)
@@ -500,21 +518,21 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text)
 
 -- delete (array)
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','e']);
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','b']);
-       delete       
---------------------
- "a"=>"1", "c"=>"3"
+     delete     
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['a','c']);
-  delete  
-----------
- "b"=>"2"
+ delete 
+--------
+ "b"=>2
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]);
@@ -524,27 +542,27 @@ select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]);
 (1 row)
 
 select delete('a=>1 , b=>2, c=>3'::hstore, '{}'::text[]);
-            delete            
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+         delete         
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','e'];
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','b'];
-      ?column?      
---------------------
- "a"=>"1", "c"=>"3"
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c'];
  ?column? 
 ----------
- "b"=>"2"
+ "b"=>2
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']];
@@ -554,9 +572,9 @@ select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']];
 (1 row)
 
 select 'a=>1 , b=>2, c=>3'::hstore - '{}'::text[];
-           ?column?           
-------------------------------
- "a"=>"1", "b"=>"2", "c"=>"3"
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c'])
@@ -575,15 +593,15 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - '{}'::text[])
 
 -- delete (hstore)
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>4, b=>2'::hstore);
-       delete        
----------------------
- "c"=>"3", "aa"=>"1"
+     delete      
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>NULL, c=>3'::hstore);
-       delete        
----------------------
- "b"=>"2", "aa"=>"1"
+     delete      
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore);
@@ -593,27 +611,27 @@ select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore);
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, 'b=>2'::hstore);
-       delete        
----------------------
- "c"=>"3", "aa"=>"1"
+     delete      
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select delete('aa=>1 , b=>2, c=>3'::hstore, ''::hstore);
-            delete             
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+         delete          
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>4, b=>2'::hstore;
-      ?column?       
----------------------
- "c"=>"3", "aa"=>"1"
+    ?column?     
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>NULL, c=>3'::hstore;
-      ?column?       
----------------------
- "b"=>"2", "aa"=>"1"
+    ?column?     
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore;
@@ -623,15 +641,15 @@ select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore;
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore;
-      ?column?       
----------------------
- "c"=>"3", "aa"=>"1"
+    ?column?     
+-----------------
+ "c"=>3, "aa"=>1
 (1 row)
 
 select 'aa=>1 , b=>2, c=>3'::hstore - ''::hstore;
-           ?column?            
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+        ?column?         
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore)
@@ -650,33 +668,33 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ''::hstore)
 
 -- ||
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f';
-                 ?column?                  
--------------------------------------------
- "b"=>"g", "aa"=>"1", "cq"=>"l", "fg"=>"f"
+               ?column?                
+---------------------------------------
+ "b"=>"g", "aa"=>1, "cq"=>"l", "fg"=>f
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'aq=>l';
-                 ?column?                  
--------------------------------------------
- "b"=>"2", "aa"=>"1", "aq"=>"l", "cq"=>"3"
+              ?column?               
+-------------------------------------
+ "b"=>2, "aa"=>1, "aq"=>"l", "cq"=>3
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || 'aa=>l';
-            ?column?            
---------------------------------
- "b"=>"2", "aa"=>"l", "cq"=>"3"
+          ?column?          
+----------------------------
+ "b"=>2, "aa"=>"l", "cq"=>3
 (1 row)
 
 select 'aa=>1 , b=>2, cq=>3'::hstore || '';
-            ?column?            
---------------------------------
- "b"=>"2", "aa"=>"1", "cq"=>"3"
+         ?column?         
+--------------------------
+ "b"=>2, "aa"=>1, "cq"=>3
 (1 row)
 
 select ''::hstore || 'cq=>l, b=>g, fg=>f';
-            ?column?            
---------------------------------
- "b"=>"g", "cq"=>"l", "fg"=>"f"
+           ?column?           
+------------------------------
+ "b"=>"g", "cq"=>"l", "fg"=>f
 (1 row)
 
 select pg_column_size(''::hstore || ''::hstore) = pg_column_size(''::hstore);
@@ -759,21 +777,21 @@ select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']);
-       slice        
---------------------
- "b"=>"2", "c"=>"3"
+     slice      
+----------------
+ "b"=>2, "c"=>3
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['aa','b']);
-        slice        
----------------------
- "b"=>"2", "aa"=>"1"
+      slice      
+-----------------
+ "b"=>2, "aa"=>1
 (1 row)
 
 select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b','aa']);
-             slice             
--------------------------------
- "b"=>"2", "c"=>"3", "aa"=>"1"
+          slice          
+-------------------------
+ "b"=>2, "c"=>3, "aa"=>1
 (1 row)
 
 select pg_column_size(slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']))
@@ -907,9 +925,9 @@ select pg_column_size(hstore(ARRAY['a','b','asd'], ARRAY['g','h','i']))
 
 -- records
 select hstore(v) from (values (1, 'foo', 1.2, 3::float8)) v(a,b,c,d);
-                   hstore                   
---------------------------------------------
- "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3"
+                hstore                
+--------------------------------------
+ "a"=>1, "b"=>"foo", "c"=>1.2, "d"=>3
 (1 row)
 
 create domain hstestdom1 as integer not null default 0;
@@ -918,9 +936,9 @@ create table testhstore1 (a integer, b text, c numeric, d float8, e hstestdom1);
 insert into testhstore0 values (1, 'foo', 1.2, 3::float8);
 insert into testhstore1 values (1, 'foo', 1.2, 3::float8);
 select hstore(v) from testhstore1 v;
-                        hstore                        
-------------------------------------------------------
- "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3", "e"=>"0"
+                    hstore                    
+----------------------------------------------
+ "a"=>1, "b"=>"foo", "c"=>1.2, "d"=>3, "e"=>0
 (1 row)
 
 select hstore(null::testhstore0);
@@ -936,7 +954,7 @@ select hstore(null::testhstore1);
 (1 row)
 
 select pg_column_size(hstore(v))
-         = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore)
+         = pg_column_size('a=>1, b=>"foo", c=>1.2, d=>3, e=>0'::hstore)
   from testhstore1 v;
  ?column? 
 ----------
@@ -1336,6 +1354,7 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
     42
 (1 row)
 
+RESET enable_seqscan;
 drop index hidx;
 create index hidx on testhstore using gin (h);
 set enable_seqscan=off;
@@ -1375,6 +1394,7 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
     42
 (1 row)
 
+RESET enable_seqscan;
 select count(*) from (select (each(h)).key from testhstore) as wow ;
  count 
 -------
@@ -1437,6 +1457,8 @@ select distinct * from (values (hstore '' || ''),('')) v(h);
 (1 row)
 
 set enable_sort = true;
+RESET enable_hashagg;
+RESET enable_sort;
 -- btree
 drop index hidx;
 create index hidx on testhstore using btree (h);
@@ -1444,7 +1466,7 @@ set enable_seqscan=off;
 select count(*) from testhstore where h #># 'p=>1';
  count 
 -------
-   125
+   884
 (1 row)
 
 select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
@@ -1453,39 +1475,63 @@ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexe
      1
 (1 row)
 
+--gin hash
+drop index hidx;
+create index hidx on testhstore using gin (h gin_hstore_hash_ops);
+set enable_seqscan=off;
+select count(*) from testhstore where h @> 'wait=>NULL';
+ count 
+-------
+     1
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC';
+ count 
+-------
+    15
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+ count 
+-------
+     2
+(1 row)
+
+RESET enable_seqscan;
+drop index hidx;
 -- json
 select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
-                                         hstore_to_json                                          
--------------------------------------------------------------------------------------------------
- {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+                                   hstore_to_json                                   
+------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
-                                              json                                               
--------------------------------------------------------------------------------------------------
- {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+                                        json                                        
+------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
-                                   hstore_to_json_loose                                   
-------------------------------------------------------------------------------------------
- {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}
+                                hstore_to_json_loose                                
+------------------------------------------------------------------------------------
+ {"b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}
 (1 row)
 
 create table test_json_agg (f1 text, f2 hstore);
 insert into test_json_agg values ('rec1','"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'),
        ('rec2','"a key" =>2, b => f, c => "null", d=> -12345, e => 012345.6, f=> -1.234, g=> 0.345e-4');
 select json_agg(q) from test_json_agg q;
-                                                          json_agg                                                          
-----------------------------------------------------------------------------------------------------------------------------
- [{"f1":"rec1","f2":{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}},      +
-  {"f1":"rec2","f2":{"b": "f", "c": "null", "d": "-12345", "e": "012345.6", "f": "-1.234", "g": "0.345e-4", "a key": "2"}}]
+                                                      json_agg                                                      
+--------------------------------------------------------------------------------------------------------------------
+ [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}},           +
+  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": 12345.6, "f": -1.234, "g": 0.0000345, "a key": 2}}]
 (1 row)
 
 select json_agg(q) from (select f1, hstore_to_json_loose(f2) as f2 from test_json_agg) q;
-                                                       json_agg                                                       
-----------------------------------------------------------------------------------------------------------------------
- [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}},       +
-  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.345e-4, "a key": 2}}]
+                                                      json_agg                                                      
+--------------------------------------------------------------------------------------------------------------------
+ [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": 12345, "f": 1.234, "g": 23450, "a key": 1}},           +
+  {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": 12345.6, "f": -1.234, "g": 0.0000345, "a key": 2}}]
 (1 row)
 
diff --git a/contrib/hstore/expected/nested.out b/contrib/hstore/expected/nested.out
new file mode 100644
index 0000000..f7df8cf
--- /dev/null
+++ b/contrib/hstore/expected/nested.out
@@ -0,0 +1,2261 @@
+SELECT 'ff => {a=>12, b=>16}'::hstore;
+          hstore          
+--------------------------
+ "ff"=>{"a"=>12, "b"=>16}
+(1 row)
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123'::hstore;
+               hstore                
+-------------------------------------
+ "ff"=>{"a"=>12, "b"=>16}, "qq"=>123
+(1 row)
+
+SELECT 'aa => {a,aaa}, qq=>{ a=>12, b=>16 , c=> { c1, c2}, d=>{d1=>d1, d2=>d2, d1=>d3} }'::hstore;
+                                             hstore                                             
+------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>12, "b"=>16, "c"=>["c1", "c2"], "d"=>{"d1"=>"d3", "d2"=>"d2"}}
+(1 row)
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+                                               hstore                                               
+----------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>"12", "b"=>"16", "c"=>["c1", "c2"], "d"=>{"d1"=>"d1", "d2"=>"d2"}}
+(1 row)
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+                                                        hstore                                                         
+-----------------------------------------------------------------------------------------------------------------------
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>"12", "b"=>"16", "c"=>["c1", "c2", ["c3"], {"c4"=>4}], "d"=>{"d1"=>"d1", "d2"=>"d2"}}
+(1 row)
+
+SELECT 'ff => {a,aaa}'::hstore;
+       hstore       
+--------------------
+ "ff"=>["a", "aaa"]
+(1 row)
+
+select 'null'::hstore;
+ hstore 
+--------
+ NULL
+(1 row)
+
+select '{null}'::hstore;
+ hstore 
+--------
+ [NULL]
+(1 row)
+
+select ''::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+select '{}'::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+--test optional outer braces
+SELECT	'a=>1'::hstore;
+ hstore 
+--------
+ "a"=>1
+(1 row)
+
+SELECT	'{a=>1}'::hstore;
+ hstore 
+--------
+ "a"=>1
+(1 row)
+
+SELECT	'{a,b}'::hstore;
+   hstore   
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT	'{a,{b}}'::hstore;
+    hstore    
+--------------
+ ["a", ["b"]]
+(1 row)
+
+SELECT	'{{a},b}'::hstore;
+    hstore    
+--------------
+ [["a"], "b"]
+(1 row)
+
+SELECT	'{a,{b},{c}}'::hstore;
+       hstore        
+---------------------
+ ["a", ["b"], ["c"]]
+(1 row)
+
+SELECT	'{{a},{b},c}'::hstore;
+       hstore        
+---------------------
+ [["a"], ["b"], "c"]
+(1 row)
+
+SELECT	'{{a},b,{c}}'::hstore;
+       hstore        
+---------------------
+ [["a"], "b", ["c"]]
+(1 row)
+
+SELECT	'{a,{b=>1}}'::hstore;
+     hstore      
+-----------------
+ ["a", {"b"=>1}]
+(1 row)
+
+SELECT	'{{a},{b=>1}}'::hstore;
+      hstore       
+-------------------
+ [["a"], {"b"=>1}]
+(1 row)
+
+SELECT	'a'::hstore;
+ hstore 
+--------
+ "a"
+(1 row)
+
+SELECT	'{a}'::hstore;
+ hstore 
+--------
+ ["a"]
+(1 row)
+
+SELECT	''::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+SELECT	'{}'::hstore;
+ hstore 
+--------
+ 
+(1 row)
+
+--nested json
+SELECT	hstore_to_json('a=>1');
+ hstore_to_json 
+----------------
+ {"a": 1}
+(1 row)
+
+SELECT	hstore_to_json('{a=>1}');
+ hstore_to_json 
+----------------
+ {"a": 1}
+(1 row)
+
+SELECT	hstore_to_json('{a,b}');
+ hstore_to_json 
+----------------
+ ["a", "b"]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b}}');
+ hstore_to_json 
+----------------
+ ["a", ["b"]]
+(1 row)
+
+SELECT	hstore_to_json('{{a},b}');
+ hstore_to_json 
+----------------
+ [["a"], "b"]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b},{c}}');
+   hstore_to_json    
+---------------------
+ ["a", ["b"], ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b},c}');
+   hstore_to_json    
+---------------------
+ [["a"], ["b"], "c"]
+(1 row)
+
+SELECT	hstore_to_json('{{a},b,{c}}');
+   hstore_to_json    
+---------------------
+ [["a"], "b", ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('{a,{b=>1}}');
+ hstore_to_json  
+-----------------
+ ["a", {"b": 1}]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b=>1}}');
+  hstore_to_json   
+-------------------
+ [["a"], {"b": 1}]
+(1 row)
+
+SELECT	hstore_to_json('{{a},{b=>1},{c}}');
+      hstore_to_json      
+--------------------------
+ [["a"], {"b": 1}, ["c"]]
+(1 row)
+
+SELECT	hstore_to_json('a');
+ hstore_to_json 
+----------------
+ "a"
+(1 row)
+
+SELECT	hstore_to_json('{a}');
+ hstore_to_json 
+----------------
+ ["a"]
+(1 row)
+
+SELECT	hstore_to_json('');
+ hstore_to_json 
+----------------
+ {}
+(1 row)
+
+SELECT	hstore_to_json('{}');
+ hstore_to_json 
+----------------
+ {}
+(1 row)
+
+SELECT hstore_to_json('"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore);
+                                                     hstore_to_json                                                      
+-------------------------------------------------------------------------------------------------------------------------
+ {"aa": ["a", "aaa"], "qq": {"a": "12", "b": "16", "c": ["c1", "c2", ["c3"], {"c4": 4}], "d": {"d1": "d1", "d2": "d2"}}}
+(1 row)
+
+--
+SELECT 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'ff', 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'qq', 
+	   ('ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'Y') IS NULL AS t, 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'x'; 
+     ?column?     | ?column? | t | ?column? 
+------------------+----------+---+----------
+ "a"=>12, "b"=>16 | 123      | t | [1, 2]
+(1 row)
+
+SELECT '[ a, b, c, d]'::hstore -> 'a';
+ ?column? 
+----------
+ a
+(1 row)
+
+--
+CREATE TABLE testtype (i int, h hstore, a int[], j json);
+INSERT INTO testtype VALUES (1, 'a=>1', '{1,2,3}', '{"x": 2}');
+SELECT populate_record(v, 'i=>2'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""a""=>1","{1,2,3}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, a=>{7,8,9}'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""a""=>1","{7,8,9}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}'::hstore) FROM testtype v;
+            populate_record            
+---------------------------------------
+ (2,"""b""=>3","{7,8,9}","{""x"": 2}")
+(1 row)
+
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}'::hstore) FROM testtype v;
+                populate_record                
+-----------------------------------------------
+ (2,"""b""=>3","{7,8,9}","{""a"": [1, 2, 3]}")
+(1 row)
+
+--complex delete
+SELECT 'b=>{a,c}'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>1'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+    ?column?     
+-----------------
+ "b"=>["a", "c"]
+(1 row)
+
+SELECT '[2,3,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[a,2,3,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[a,a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>1'::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT ''::hstore - 'a'::text;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '{a, 1 , b,2, c,3}'::hstore - ARRAY['d','b'];
+      ?column?       
+---------------------
+ ["a", 1, 2, "c", 3]
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v'::hstore;
+       ?column?        
+-----------------------
+ "a"=>[1, 2], "b"=>"c"
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>23'::hstore;
+       ?column?        
+-----------------------
+ "a"=>[1, 2], "b"=>"c"
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>{1,2}'::hstore;
+            ?column?            
+--------------------------------
+ "a"=>[1, 2], "b"=>"c", "v"=>23
+(1 row)
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'a=>{1,2}'::hstore;
+     ?column?      
+-------------------
+ "b"=>"c", "v"=>23
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v'::hstore;
+          ?column?           
+-----------------------------
+ ["a", [1, 2], 23, "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v=>23'::hstore;
+        ?column?         
+-------------------------
+ ["a", [1, 2], "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,23]'::hstore;
+        ?column?         
+-------------------------
+ ["a", [1, 2], "b", "c"]
+(1 row)
+
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,{1,2}]'::hstore;
+      ?column?       
+---------------------
+ ["a", 23, "b", "c"]
+(1 row)
+
+--joining
+SELECT 'aa=>1 , b=>2, cq=>3'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+                   ?column?                    
+-----------------------------------------------
+ "1"=>2, "b"=>"g", "aa"=>1, "cq"=>"l", "fg"=>f
+(1 row)
+
+SELECT '{aa,1 , b,2, cq,3}'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+                            ?column?                            
+----------------------------------------------------------------
+ ["aa", 1, "b", 2, "cq", 3, "cq", "l", "b", "g", "fg", f, 1, 2]
+(1 row)
+
+SELECT  'x'::hstore || 'a=>"1"':: hstore;
+    ?column?     
+-----------------
+ ["x", "a", "1"]
+(1 row)
+
+--slice
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
+   slice_array    
+------------------
+ {NULL,NULL,NULL}
+(1 row)
+
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+   slice_array    
+------------------
+ {NULL,NULL,NULL}
+(1 row)
+
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['b','c']);
+ slice_array 
+-------------
+ {2,3}
+(1 row)
+
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+ slice_array 
+-------------
+ {b,c}
+(1 row)
+
+SELECT slice_array(hstore 'aa=>1, b=>{2=>1}, c=>{1,2}', ARRAY['b','c']);
+      slice_array      
+-----------------------
+ {"\"2\"=>1","[1, 2]"}
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['g','h','i']);
+ slice 
+-------
+ 
+(1 row)
+
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+ slice 
+-------
+ 
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['b','c']);
+     slice      
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+   slice    
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT slice(hstore '{aa=>1, b=>{2=>1}, c=>{1,2}}', ARRAY['b','c']);
+           slice            
+----------------------------
+ "b"=>{"2"=>1}, "c"=>[1, 2]
+(1 row)
+
+--to array
+SELECT %% 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL';
+                ?column?                
+----------------------------------------
+ {b,"[\"a\", \"n\"]",aa,1,cq,l,fg,NULL}
+(1 row)
+
+SELECT %% '{aa,1, cq,l, b,g, fg,NULL}';
+        ?column?         
+-------------------------
+ {aa,1,cq,l,b,g,fg,NULL}
+(1 row)
+
+SELECT hstore_to_matrix( 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL');
+                hstore_to_matrix                
+------------------------------------------------
+ {{b,"[\"a\", \"n\"]"},{aa,1},{cq,l},{fg,NULL}}
+(1 row)
+
+SELECT hstore_to_matrix( '{aa,1, cq,l, b,g, fg,NULL}');
+        hstore_to_matrix         
+---------------------------------
+ {{aa,1},{cq,l},{b,g},{fg,NULL}}
+(1 row)
+
+--contains
+SELECT 'a=>b'::hstore @> 'a=>b, c=>b';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>b'::hstore @> 'a=>b';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{2,1}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1,2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1=>2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1=>2}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{a,b}'::hstore @> '{a,b, c,b}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{a,b, c,b}'::hstore @> '{a,b}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{a,b, c,{1,2}}'::hstore @> '{a,{1,2}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{a,b, c,{1,2}}'::hstore @> '{b,{1,2}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{2}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{3}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{c=>3}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4}}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},3}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+-- %>
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'n';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'a';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'b';
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'c';
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd';
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd' %> '1';
+ ?column? 
+----------
+ [2, 3]
+(1 row)
+
+SELECT '[1,2,3,{a,b}]'::hstore %> '1';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '["1",2,3,{a,b}]'::hstore %> '1';
+ ?column? 
+----------
+ "1"
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 3;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 2;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 1;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 0;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 3;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 2;
+ ?column? 
+----------
+ "c"
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 1;
+ ?column? 
+----------
+ "b"
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 0;
+ ?column? 
+----------
+ "a"
+(1 row)
+
+-- ->
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 3;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 2;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 1;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 0;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 5;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 4;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 3;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 2;
+ ?column? 
+----------
+ c
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 1;
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 0;
+ ?column? 
+----------
+ a
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -6;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -5;
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -4;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -3;
+ ?column? 
+----------
+ "1"=>2
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -2;
+  ?column?   
+-------------
+ "1"=>[2, 3]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -1;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -6;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -5;
+ ?column? 
+----------
+ a
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -4;
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -3;
+ ?column? 
+----------
+ c
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -2;
+ ?column? 
+----------
+ [1, 2]
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -1;
+ ?column? 
+----------
+ 
+(1 row)
+
+-- #>
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{a}';
+ ?column? 
+----------
+ b
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c}';
+ ?column?  
+-----------
+ [1, 2, 3]
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 0}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 1}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 2}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 3}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -1}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -2}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -3}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -4}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{0}';
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{3}';
+ ?column? 
+----------
+ [3, 4]
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4}';
+  ?column?   
+-------------
+ "5"=>"five"
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4,5}';
+ ?column? 
+----------
+ five
+(1 row)
+
+-- #%>
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{a}';
+ ?column? 
+----------
+ "b"
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c}';
+ ?column?  
+-----------
+ [1, 2, 3]
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 0}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 1}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 2}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 3}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -1}';
+ ?column? 
+----------
+ 3
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -2}';
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -3}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -4}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{0}';
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{3}';
+ ?column? 
+----------
+ [3, 4]
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4}';
+  ?column?   
+-------------
+ "5"=>"five"
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4,5}';
+ ?column? 
+----------
+ "five"
+(1 row)
+
+-- ?
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 5;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 0;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 5;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 0;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -6;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -5;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -6;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -5;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -4;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -3;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{0}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{a}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{b}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 0}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 1}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 2}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 3}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -1}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -2}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -3}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -4}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -5}'::text[];
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{0}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{3}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4,5}'::text[];
+ ?column? 
+----------
+ t
+(1 row)
+
+--deep delete
+SELECT 'a=>1'::hstore #- '{x}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{a}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{NULL}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a}';
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b}';
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c}';
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{x,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{a,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1'::hstore #- '{NULL,1}';
+ ?column? 
+----------
+ "a"=>1
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c,1}';
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT '[a]'::hstore #- '{2}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a]'::hstore #- '{1}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a]'::hstore #- '{0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore #- '{-1}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[a]'::hstore #- '{-2}';
+ ?column? 
+----------
+ ["a"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{3}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{2}';
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{1}';
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{0}';
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-1}';
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-2}';
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-3}';
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{-4}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore #- '{0,0}';
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{x}';
+                             ?column?                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{a}';
+                         ?column?                          
+-----------------------------------------------------------
+ "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b}';
+                       ?column?                       
+------------------------------------------------------
+ "a"=>1, "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c}';
+                      ?column?                      
+----------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d}';
+                   ?column?                    
+-----------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, 0}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}' #- '{b, -1}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>NULL, "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 1}';
+                           ?column?                            
+---------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>NULL, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 2}';
+                             ?column?                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, -2}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 1}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}';
+                            ?column?                            
+----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[3]}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>NULL}, "n"=>NULL
+(1 row)
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}' #- '{d, 1, 0}';
+                            ?column?                             
+-----------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>NULL}, "n"=>NULL
+(1 row)
+
+-- delete(int)
+SELECT '[a,b,c]'::hstore - 3;
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 2;
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 1;
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - 0;
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -1;
+  ?column?  
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -2;
+  ?column?  
+------------
+ ["a", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -3;
+  ?column?  
+------------
+ ["b", "c"]
+(1 row)
+
+SELECT '[a,b,c]'::hstore - -4;
+    ?column?     
+-----------------
+ ["a", "b", "c"]
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 3;
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 2;
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 1;
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 0;
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -1;
+    ?column?    
+----------------
+ "a"=>1, "b"=>2
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -2;
+    ?column?    
+----------------
+ "a"=>1, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -3;
+    ?column?    
+----------------
+ "b"=>2, "c"=>3
+(1 row)
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - -4;
+        ?column?        
+------------------------
+ "a"=>1, "b"=>2, "c"=>3
+(1 row)
+
+--replace
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{1,2,3}');
+                                replace                                 
+------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>[1, 2, 3]
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b,-1}', '{1,2,3}');
+                                  replace                                  
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, [1, 2, 3]], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1,0}', '{1,2,3}');
+                                  replace                                  
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[[1, 2, 3], 3]}, "n"=>NULL
+(1 row)
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,NULL,0}', '{1,2,3}');
+                              replace                              
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+--deep concat
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'n=>not_null');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>"not_null"
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'n=>not_null');
+                                  concat_path                                   
+--------------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>{"n"=>"not_null"}
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'not_null');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>"not_null"
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{not_null}');
+                                concat_path                                
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>["not_null"]
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'b=>{3,4}');
+                            concat_path                            
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[3, 4], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b}', '{3,4}');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2, 3, 4], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '{4,5}');
+                               concat_path                               
+-------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3, 4, 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '4=>5');
+                                concat_path                                
+---------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3, "4", 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d}', '2=>{4,5}');
+                                  concat_path                                   
+--------------------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3], "2"=>[4, 5]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{NULL,1}', '4=>5');
+                            concat_path                            
+-------------------------------------------------------------------
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL
+(1 row)
+
+SELECT concat_path('x'::hstore, '{}'::text[], 'a=>"1"':: hstore);
+   concat_path   
+-----------------
+ ["x", "a", "1"]
+(1 row)
+
+--cast 
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::text)::hstore AS err;
+ERROR:  bad hstore representation
+DETAIL:  syntax error, unexpected STRING_P, expecting '}' or ',' at end of input
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json)::hstore AS ok;
+                         ok                         
+----------------------------------------------------
+ "f2"=>{"f3"=>1}, "f4"=>{"f5"=>99, "f6"=>"stringy"}
+(1 row)
+
+--hvals
+SELECT q->'tags' FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore) AS q;
+ ?column? 
+----------
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+                      q                       
+----------------------------------------------
+ [{"sh"=>2, "tags"=>1}, {"sh"=>4, "tags"=>3}]
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+ "sh"=>4, "tags"=>3
+(2 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+ q 
+---
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+                                q                                
+-----------------------------------------------------------------
+ "1"=>"first", "a"=>{"b"=>"c", "c"=>"b"}, "b"=>[1, 2], "c"=>"cc"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+    q    
+---------
+ "first"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+         q          
+--------------------
+ "b"=>"c", "c"=>"b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+   q    
+--------
+ [1, 2]
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+  q   
+------
+ "cc"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "first"
+ "b"=>"c", "c"=>"b"
+ [1, 2]
+ "cc"
+(4 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+  q  
+-----
+ "b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+  q  
+-----
+ "b"
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+ q 
+---
+ 1
+ 2
+(2 rows)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+ q 
+---
+ 2
+(1 row)
+
+SELECT q FROM hvals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+    q    
+---------
+ "first"
+ 2
+(2 rows)
+
+--svals path
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+                      q                       
+----------------------------------------------
+ [{"sh"=>2, "tags"=>1}, {"sh"=>4, "tags"=>3}]
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+         q          
+--------------------
+ "sh"=>4, "tags"=>3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ "sh"=>2, "tags"=>1
+ "sh"=>4, "tags"=>3
+(2 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+ q 
+---
+ 1
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+ q 
+---
+ 3
+(1 row)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+ q 
+---
+(0 rows)
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+ q 
+---
+ 1
+ 3
+(2 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+                                q                                
+-----------------------------------------------------------------
+ "1"=>"first", "a"=>{"b"=>"c", "c"=>"b"}, "b"=>[1, 2], "c"=>"cc"
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+   q   
+-------
+ first
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+         q          
+--------------------
+ "b"=>"c", "c"=>"b"
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+   q    
+--------
+ [1, 2]
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+ q  
+----
+ cc
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+         q          
+--------------------
+ first
+ "b"=>"c", "c"=>"b"
+ [1, 2]
+ cc
+(4 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+ q 
+---
+ b
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+ q 
+---
+ b
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+ q 
+---
+ 1
+ 2
+(2 rows)
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+ q 
+---
+ 2
+(1 row)
+
+SELECT q FROM svals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+   q   
+-------
+ first
+ 2
+(2 rows)
+
+--each
+SELECT * FROM each('a=>b, c=>cc'::hstore) AS q;
+ key | value 
+-----+-------
+ a   | b
+ c   | cc
+(2 rows)
+
+SELECT * FROM each('[a, b, c, cc]'::hstore) AS q;
+ key | value 
+-----+-------
+     | a
+     | b
+     | c
+     | cc
+(4 rows)
+
+SELECT * FROM each('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+ key |              value               
+-----+----------------------------------
+ 1   | first
+ a   | "1"=>"first", "b"=>"c", "c"=>"b"
+ b   | [1, 2]
+ c   | cc
+ n   | 
+(5 rows)
+
+SELECT * FROM each_hstore('a=>b, c=>cc'::hstore) AS q;
+ key | value 
+-----+-------
+ a   | "b"
+ c   | "cc"
+(2 rows)
+
+SELECT * FROM each_hstore('[a, b, c, cc]'::hstore) AS q;
+ key | value 
+-----+-------
+     | "a"
+     | "b"
+     | "c"
+     | "cc"
+(4 rows)
+
+SELECT * FROM each_hstore('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+ key |              value               
+-----+----------------------------------
+ 1   | "first"
+ a   | "1"=>"first", "b"=>"c", "c"=>"b"
+ b   | [1, 2]
+ c   | "cc"
+ n   | 
+(5 rows)
+
+--decoration
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]]'::hstore AS h, '[a, {b=>c}, [c, d, e]]'::hstore AS a;
+                  h                   |                 a                  
+--------------------------------------+------------------------------------
+ "a"=>1, "b"=>{"c"=>3}, "d"=>[4, [5]] | ["a", {"b"=>"c"}, ["c", "d", "e"]]
+(1 row)
+
+SET hstore.pretty_print = true;
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]], e=>[1,2,3,4], f=>g, g=>j'::hstore AS h, 
+	   '[a, {b=>c, c=>d}, [c, d, e, [1,2], h, {f=>g, g=>f}]]'::hstore AS a;
+     h      |           a            
+------------+------------------------
+ "a"=>1,   +| [                     +
+ "b"=>     +|     "a",              +
+ {         +|     {                 +
+     "c"=>3+|         "b"=>"c",     +
+ },        +|         "c"=>"d"      +
+ "d"=>     +|     },                +
+ [         +|     [                 +
+     4,    +|         "c",          +
+     [     +|         "d",          +
+         5 +|         "e",          +
+     ]     +|         [             +
+ ],        +|             1,        +
+ "e"=>     +|             2         +
+ [         +|         ],            +
+     1,    +|         "h",          +
+     2,    +|         {             +
+     3,    +|             "f"=>"g", +
+     4     +|             "g"=>f    +
+ ],        +|         }             +
+ "f"=>"g", +|     ]                 +
+ "g"=>"j"   | ]
+(1 row)
+
+RESET hstore.pretty_print;
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore);
+                             hstore_print                              
+-----------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>[1, 2, 3, "3", "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true );
+                           hstore_print                            
+-------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>[1, 2, 3, 3, "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true );
+                              hstore_print                               
+-------------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>[1, 2, 3, "3", "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, array_curly_braces := true );
+                             hstore_print                              
+-----------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>{1, 2, 3, "3", "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": "f", "123": "string", "arr": [1, 2, 3, "3", "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, loose := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": false, "123": "string", "arr": [1, 2, 3, 3, "x"]
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, root_hash_decorated := true );
+                                 hstore_print                                  
+-------------------------------------------------------------------------------
+ {"a": true, "f": true, "t": "f", "123": "string", "arr": [1, 2, 3, "3", "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, array_curly_braces := true );
+                                hstore_print                                 
+-----------------------------------------------------------------------------
+ "a": true, "f": true, "t": "f", "123": "string", "arr": {1, 2, 3, "3", "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, root_hash_decorated := true );
+                            hstore_print                             
+---------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>[1, 2, 3, 3, "x"]}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, array_curly_braces := true );
+                           hstore_print                            
+-------------------------------------------------------------------
+ "a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>{1, 2, 3, 3, "x"}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true );
+                              hstore_print                               
+-------------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>{1, 2, 3, "3", "x"}}
+(1 row)
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true, loose := true);
+                            hstore_print                             
+---------------------------------------------------------------------
+ {"a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>{1, 2, 3, 3, "x"}}
+(1 row)
+
diff --git a/contrib/hstore/expected/types.out b/contrib/hstore/expected/types.out
new file mode 100644
index 0000000..4206c58
--- /dev/null
+++ b/contrib/hstore/expected/types.out
@@ -0,0 +1,762 @@
+SELECT '"foo"=>true'::hstore;
+  hstore  
+----------
+ "foo"=>t
+(1 row)
+
+SELECT 'foo=>true'::hstore;
+  hstore  
+----------
+ "foo"=>t
+(1 row)
+
+SELECT '"true"=>true'::hstore;
+  hstore   
+-----------
+ "true"=>t
+(1 row)
+
+SELECT 'true=>true'::hstore;
+  hstore   
+-----------
+ "true"=>t
+(1 row)
+
+SELECT '"t"=>true'::hstore;
+ hstore 
+--------
+ "t"=>t
+(1 row)
+
+SELECT 't=>true'::hstore;
+ hstore 
+--------
+ "t"=>t
+(1 row)
+
+SELECT '"false"=>true'::hstore;
+   hstore   
+------------
+ "false"=>t
+(1 row)
+
+SELECT 'false=>true'::hstore;
+   hstore   
+------------
+ "false"=>t
+(1 row)
+
+SELECT '"f"=>true'::hstore;
+ hstore 
+--------
+ "f"=>t
+(1 row)
+
+SELECT 'f=>true'::hstore;
+ hstore 
+--------
+ "f"=>t
+(1 row)
+
+SELECT '"foo"=>false'::hstore;
+  hstore  
+----------
+ "foo"=>f
+(1 row)
+
+SELECT 'foo=>false'::hstore;
+  hstore  
+----------
+ "foo"=>f
+(1 row)
+
+SELECT '"false"=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT 'false=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT '"t"=>false'::hstore;
+ hstore 
+--------
+ "t"=>f
+(1 row)
+
+SELECT 't=>false'::hstore;
+ hstore 
+--------
+ "t"=>f
+(1 row)
+
+SELECT '"false"=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT 'false=>false'::hstore;
+   hstore   
+------------
+ "false"=>f
+(1 row)
+
+SELECT '"f"=>false'::hstore;
+ hstore 
+--------
+ "f"=>f
+(1 row)
+
+SELECT 'f=>false'::hstore;
+ hstore 
+--------
+ "f"=>f
+(1 row)
+
+SELECT '"1"=>x'::hstore;
+  hstore  
+----------
+ "1"=>"x"
+(1 row)
+
+SELECT '1=>x'::hstore;
+  hstore  
+----------
+ "1"=>"x"
+(1 row)
+
+SELECT 'foo=>1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>1.'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>1.0'::hstore;
+   hstore   
+------------
+ "foo"=>1.0
+(1 row)
+
+SELECT 'foo=>1.01'::hstore;
+   hstore    
+-------------
+ "foo"=>1.01
+(1 row)
+
+SELECT 'foo=>1.01e'::hstore;
+     hstore     
+----------------
+ "foo"=>"1.01e"
+(1 row)
+
+SELECT 'foo=>1.01e1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>1.01e+1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>1.01e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>0.101
+(1 row)
+
+SELECT 'foo=>.1'::hstore;
+   hstore   
+------------
+ "foo"=>0.1
+(1 row)
+
+SELECT 'foo=>.1e'::hstore;
+    hstore    
+--------------
+ "foo"=>".1e"
+(1 row)
+
+SELECT 'foo=>.1e1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>.1e+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>0.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>00.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+1.'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+1.0'::hstore;
+   hstore   
+------------
+ "foo"=>1.0
+(1 row)
+
+SELECT 'foo=>+1.01'::hstore;
+   hstore    
+-------------
+ "foo"=>1.01
+(1 row)
+
+SELECT 'foo=>+1.01e'::hstore;
+     hstore      
+-----------------
+ "foo"=>"+1.01e"
+(1 row)
+
+SELECT 'foo=>+1.01e1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>+1.01e+1'::hstore;
+   hstore    
+-------------
+ "foo"=>10.1
+(1 row)
+
+SELECT 'foo=>+1.01e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>0.101
+(1 row)
+
+SELECT 'foo=>+.1'::hstore;
+   hstore   
+------------
+ "foo"=>0.1
+(1 row)
+
+SELECT 'foo=>+.1e'::hstore;
+    hstore     
+---------------
+ "foo"=>"+.1e"
+(1 row)
+
+SELECT 'foo=>+.1e1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+.1e+1'::hstore;
+  hstore  
+----------
+ "foo"=>1
+(1 row)
+
+SELECT 'foo=>+.1e-1'::hstore;
+   hstore    
+-------------
+ "foo"=>0.01
+(1 row)
+
+SELECT 'foo=>-1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-1.'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-1.0'::hstore;
+   hstore    
+-------------
+ "foo"=>-1.0
+(1 row)
+
+SELECT 'foo=>-1.01'::hstore;
+    hstore    
+--------------
+ "foo"=>-1.01
+(1 row)
+
+SELECT 'foo=>-1.01e'::hstore;
+     hstore      
+-----------------
+ "foo"=>"-1.01e"
+(1 row)
+
+SELECT 'foo=>-1.01e1'::hstore;
+    hstore    
+--------------
+ "foo"=>-10.1
+(1 row)
+
+SELECT 'foo=>-1.01e+1'::hstore;
+    hstore    
+--------------
+ "foo"=>-10.1
+(1 row)
+
+SELECT 'foo=>-1.01e-1'::hstore;
+    hstore     
+---------------
+ "foo"=>-0.101
+(1 row)
+
+SELECT 'foo=>-.1'::hstore;
+   hstore    
+-------------
+ "foo"=>-0.1
+(1 row)
+
+SELECT 'foo=>-.1e'::hstore;
+    hstore     
+---------------
+ "foo"=>"-.1e"
+(1 row)
+
+SELECT 'foo=>-.1e1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-.1e+1'::hstore;
+  hstore   
+-----------
+ "foo"=>-1
+(1 row)
+
+SELECT 'foo=>-.1e-1'::hstore;
+    hstore    
+--------------
+ "foo"=>-0.01
+(1 row)
+
+SELECT 'foo=>1e2000'::hstore;
+     hstore      
+-----------------
+ "foo"=>"1e2000"
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'foo';
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'bar';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 0;
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 1;
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'foo';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'bar';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 0;
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 1;
+   ?column?    
+---------------
+ 1000000000000
+(1 row)
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 0}';
+ ?column? 
+----------
+         
+(1 row)
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 1}';
+    ?column?    
+----------------
+ 0.000000000001
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'foo';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'foo';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, t, bar, x]'::hstore ?> 1;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 1}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'foo';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 1;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'foo';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'bar';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 0;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT '[foo, f, bar, x]'::hstore ?> 1;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 0}';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 1}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT hstore_typeof('a=>b') AS hash;
+ hash 
+------
+ hash
+(1 row)
+
+SELECT hstore_typeof('{a=>b}') AS hash;
+ hash 
+------
+ hash
+(1 row)
+
+SELECT hstore_typeof('{a, b}') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('{{a=>b}}') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('[a, b]') AS array;
+ array 
+-------
+ array
+(1 row)
+
+SELECT hstore_typeof('') AS "NULL";
+ NULL 
+------
+ 
+(1 row)
+
+SELECT hstore_typeof('NULL') AS "null";
+ null 
+------
+ null
+(1 row)
+
+SELECT hstore_typeof('1.0') AS numeric;
+ numeric 
+---------
+ numeric
+(1 row)
+
+SELECT hstore_typeof('t') AS bool;
+ bool 
+------
+ bool
+(1 row)
+
+SELECT hstore_typeof('f') AS bool;
+ bool 
+------
+ bool
+(1 row)
+
+SELECT hstore('xxx', 't'::bool);
+  hstore  
+----------
+ "xxx"=>t
+(1 row)
+
+SELECT hstore('xxx', 'f'::bool);
+  hstore  
+----------
+ "xxx"=>f
+(1 row)
+
+SELECT hstore('xxx', 3.14);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore('xxx', 3.14::numeric);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore('xxx', '3.14'::numeric);
+   hstore    
+-------------
+ "xxx"=>3.14
+(1 row)
+
+SELECT hstore(NULL);
+ hstore 
+--------
+ 
+(1 row)
+
+SELECT hstore('NULL');
+ hstore 
+--------
+ NULL
+(1 row)
+
+SELECT hstore('t'::bool) AS "true", hstore('f'::bool) AS "false";
+ true | false 
+------+-------
+ t    | f
+(1 row)
+
+SELECT hstore(3.14), hstore(3.14::numeric), hstore('3.14'::numeric);
+ hstore | hstore | hstore 
+--------+--------+--------
+ 3.14   | 3.14   | 3.14
+(1 row)
+
+SELECT hstore('xxx', 'foo=>t, bar=>3.14, zzz=>xxx'::hstore);
+                    hstore                    
+----------------------------------------------
+ "xxx"=>{"bar"=>3.14, "foo"=>t, "zzz"=>"xxx"}
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int2[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int4[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int8[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float4[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float8[]);
+     array_to_hstore     
+-------------------------
+ [[1, 1, 4], [23, 3, 5]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,f},{f,t,NULL}}'::bool[]);
+      array_to_hstore      
+---------------------------
+ [[t, t, f], [f, t, NULL]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::text[]);
+           array_to_hstore           
+-------------------------------------
+ [["1", "1", "4"], ["23", "3", "5"]]
+(1 row)
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::varchar[]);
+           array_to_hstore           
+-------------------------------------
+ [["1", "1", "4"], ["23", "3", "5"]]
+(1 row)
+
+SELECT array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]);
+                       array_to_hstore                       
+-------------------------------------------------------------
+ [[[1, 11], [1, 1], [4, 41], [23, 231]], [[3, 31], [5, 51]]]
+(1 row)
+
+SELECT hstore('array', array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]));
+                                hstore                                
+----------------------------------------------------------------------
+ "array"=>[[[1, 11], [1, 1], [4, 41], [23, 231]], [[3, 31], [5, 51]]]
+(1 row)
+
+SELECT 'a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore;
+                        hstore                         
+-------------------------------------------------------
+ "a"=>"00012333", "b"=>"12233", "c"=>12333, "d"=>12233
+(1 row)
+
+SELECT hstore_to_json('a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore);
+                     hstore_to_json                      
+---------------------------------------------------------
+ {"a": "00012333", "b": "12233", "c": 12333, "d": 12233}
+(1 row)
+
+SELECT hstore_to_json_loose('a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore);
+                 hstore_to_json_loose                  
+-------------------------------------------------------
+ {"a": "00012333", "b": 12233, "c": 12333, "d": 12233}
+(1 row)
+
diff --git a/contrib/hstore/hstore--1.2--1.3.sql b/contrib/hstore/hstore--1.2--1.3.sql
new file mode 100644
index 0000000..b913a08
--- /dev/null
+++ b/contrib/hstore/hstore--1.2--1.3.sql
@@ -0,0 +1,338 @@
+/* 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 FUNCTION hstore2jsonb(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore2jsonb'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS jsonb)
+  WITH FUNCTION hstore2jsonb(hstore);
+
+CREATE FUNCTION jsonb2hstore(jsonb)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'jsonb2hstore'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (jsonb AS hstore)
+  WITH FUNCTION jsonb2hstore(jsonb);
+
diff --git a/contrib/hstore/hstore--1.2.sql b/contrib/hstore/hstore--1.2.sql
deleted file mode 100644
index f415a72..0000000
--- a/contrib/hstore/hstore--1.2.sql
+++ /dev/null
@@ -1,537 +0,0 @@
-/* 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
-
-CREATE TYPE hstore;
-
-CREATE FUNCTION hstore_in(cstring)
-RETURNS hstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_out(hstore)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_recv(internal)
-RETURNS hstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_send(hstore)
-RETURNS bytea
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE TYPE hstore (
-        INTERNALLENGTH = -1,
-        INPUT = hstore_in,
-        OUTPUT = hstore_out,
-        RECEIVE = hstore_recv,
-        SEND = hstore_send,
-        STORAGE = extended
-);
-
-CREATE FUNCTION hstore_version_diag(hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_version_diag'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION fetchval(hstore,text)
-RETURNS text
-AS 'MODULE_PATHNAME','hstore_fetchval'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR -> (
-	LEFTARG = hstore,
-	RIGHTARG = text,
-	PROCEDURE = fetchval
-);
-
-CREATE FUNCTION slice_array(hstore,text[])
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_slice_to_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR -> (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = slice_array
-);
-
-CREATE FUNCTION slice(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_slice_to_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION isexists(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION exist(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists'
-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'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?| (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = exists_any,
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION exists_all(hstore,text[])
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_exists_all'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR ?& (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = exists_all,
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION isdefined(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_defined'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION defined(hstore,text)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_defined'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION delete(hstore,hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_delete_hstore'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = text,
-	PROCEDURE = delete
-);
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = text[],
-	PROCEDURE = delete
-);
-
-CREATE OPERATOR - (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = delete
-);
-
-CREATE FUNCTION hs_concat(hstore,hstore)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_concat'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR || (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_concat
-);
-
-CREATE FUNCTION hs_contains(hstore,hstore)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_contains'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hs_contained(hstore,hstore)
-RETURNS bool
-AS 'MODULE_PATHNAME','hstore_contained'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR @> (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contains,
-	COMMUTATOR = '<@',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE OPERATOR <@ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contained,
-	COMMUTATOR = '@>',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
--- obsolete:
-CREATE OPERATOR @ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contains,
-	COMMUTATOR = '~',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~ (
-	LEFTARG = hstore,
-	RIGHTARG = hstore,
-	PROCEDURE = hs_contained,
-	COMMUTATOR = '@',
-	RESTRICT = contsel,
-	JOIN = contjoinsel
-);
-
-CREATE FUNCTION tconvert(text,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_text'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text,text)
-RETURNS hstore
-AS 'MODULE_PATHNAME','hstore_from_text'
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
-
-CREATE FUNCTION hstore(text[],text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_arrays'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null)
-
-CREATE FUNCTION hstore(text[])
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_array'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE CAST (text[] AS hstore)
-  WITH FUNCTION hstore(text[]);
-
-CREATE FUNCTION hstore_to_json(hstore)
-RETURNS json
-AS 'MODULE_PATHNAME', 'hstore_to_json'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE CAST (hstore AS json)
-  WITH FUNCTION hstore_to_json(hstore);
-
-CREATE FUNCTION hstore_to_json_loose(hstore)
-RETURNS json
-AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION hstore(record)
-RETURNS hstore
-AS 'MODULE_PATHNAME', 'hstore_from_record'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype)
-
-CREATE FUNCTION hstore_to_array(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_to_array'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %% (
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_to_array
-);
-
-CREATE FUNCTION hstore_to_matrix(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_to_matrix'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR %# (
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_to_matrix
-);
-
-CREATE FUNCTION akeys(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_akeys'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION avals(hstore)
-RETURNS text[]
-AS 'MODULE_PATHNAME','hstore_avals'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION skeys(hstore)
-RETURNS setof text
-AS 'MODULE_PATHNAME','hstore_skeys'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION svals(hstore)
-RETURNS setof text
-AS 'MODULE_PATHNAME','hstore_svals'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION each(IN hs hstore,
-    OUT key text,
-    OUT value text)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME','hstore_each'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION populate_record(anyelement,hstore)
-RETURNS anyelement
-AS 'MODULE_PATHNAME', 'hstore_populate_record'
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore)
-
-CREATE OPERATOR #= (
-	LEFTARG = anyelement,
-	RIGHTARG = hstore,
-	PROCEDURE = populate_record
-);
-
--- btree support
-
-CREATE FUNCTION hstore_eq(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_eq'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_ne(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_ne'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_gt(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_gt'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_ge(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_ge'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_lt(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_lt'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_le(hstore,hstore)
-RETURNS boolean
-AS 'MODULE_PATHNAME','hstore_le'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION hstore_cmp(hstore,hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_cmp'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR = (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_eq,
-       COMMUTATOR = =,
-       NEGATOR = <>,
-       RESTRICT = eqsel,
-       JOIN = eqjoinsel,
-       MERGES,
-       HASHES
-);
-CREATE OPERATOR <> (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_ne,
-       COMMUTATOR = <>,
-       NEGATOR = =,
-       RESTRICT = neqsel,
-       JOIN = neqjoinsel
-);
-
--- the comparison operators have funky names (and are undocumented)
--- in an attempt to discourage anyone from actually using them. they
--- only exist to support the btree opclass
-
-CREATE OPERATOR #<# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_lt,
-       COMMUTATOR = #>#,
-       NEGATOR = #>=#,
-       RESTRICT = scalarltsel,
-       JOIN = scalarltjoinsel
-);
-CREATE OPERATOR #<=# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_le,
-       COMMUTATOR = #>=#,
-       NEGATOR = #>#,
-       RESTRICT = scalarltsel,
-       JOIN = scalarltjoinsel
-);
-CREATE OPERATOR #># (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_gt,
-       COMMUTATOR = #<#,
-       NEGATOR = #<=#,
-       RESTRICT = scalargtsel,
-       JOIN = scalargtjoinsel
-);
-CREATE OPERATOR #>=# (
-       LEFTARG = hstore,
-       RIGHTARG = hstore,
-       PROCEDURE = hstore_ge,
-       COMMUTATOR = #<=#,
-       NEGATOR = #<#,
-       RESTRICT = scalargtsel,
-       JOIN = scalargtjoinsel
-);
-
-CREATE OPERATOR CLASS btree_hstore_ops
-DEFAULT FOR TYPE hstore USING btree
-AS
-	OPERATOR	1	#<# ,
-	OPERATOR	2	#<=# ,
-	OPERATOR	3	= ,
-	OPERATOR	4	#>=# ,
-	OPERATOR	5	#># ,
-	FUNCTION	1	hstore_cmp(hstore,hstore);
-
--- hash support
-
-CREATE FUNCTION hstore_hash(hstore)
-RETURNS integer
-AS 'MODULE_PATHNAME','hstore_hash'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE OPERATOR CLASS hash_hstore_ops
-DEFAULT FOR TYPE hstore USING hash
-AS
-	OPERATOR	1	= ,
-	FUNCTION	1	hstore_hash(hstore);
-
--- GiST support
-
-CREATE TYPE ghstore;
-
-CREATE FUNCTION ghstore_in(cstring)
-RETURNS ghstore
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION ghstore_out(ghstore)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE TYPE ghstore (
-        INTERNALLENGTH = -1,
-        INPUT = ghstore_in,
-        OUTPUT = ghstore_out
-);
-
-CREATE FUNCTION ghstore_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_union(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_same(internal, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR CLASS gist_hstore_ops
-DEFAULT FOR TYPE hstore USING gist
-AS
-	OPERATOR        7       @> ,
-	OPERATOR        9       ?(hstore,text) ,
-	OPERATOR        10      ?|(hstore,text[]) ,
-	OPERATOR        11      ?&(hstore,text[]) ,
-        --OPERATOR        8       <@ ,
-        OPERATOR        13      @ ,
-        --OPERATOR        14      ~ ,
-        FUNCTION        1       ghstore_consistent (internal, internal, int, oid, internal),
-        FUNCTION        2       ghstore_union (internal, internal),
-        FUNCTION        3       ghstore_compress (internal),
-        FUNCTION        4       ghstore_decompress (internal),
-        FUNCTION        5       ghstore_penalty (internal, internal, internal),
-        FUNCTION        6       ghstore_picksplit (internal, internal),
-        FUNCTION        7       ghstore_same (internal, internal, internal),
-        STORAGE         ghstore;
-
--- GIN support
-
-CREATE FUNCTION gin_extract_hstore(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR CLASS gin_hstore_ops
-DEFAULT FOR TYPE hstore USING gin
-AS
-	OPERATOR        7       @>,
-	OPERATOR        9       ?(hstore,text),
-	OPERATOR        10      ?|(hstore,text[]),
-	OPERATOR        11      ?&(hstore,text[]),
-	FUNCTION        1       bttextcmp(text,text),
-	FUNCTION        2       gin_extract_hstore(internal, internal),
-	FUNCTION        3       gin_extract_hstore_query(internal, internal, int2, internal, internal),
-	FUNCTION        4       gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
-	STORAGE         text;
diff --git a/contrib/hstore/hstore--1.3.sql b/contrib/hstore/hstore--1.3.sql
new file mode 100644
index 0000000..153cb74
--- /dev/null
+++ b/contrib/hstore/hstore--1.3.sql
@@ -0,0 +1,854 @@
+/* contrib/hstore/hstore--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION hstore" to load this file. \quit
+
+CREATE TYPE hstore;
+
+CREATE FUNCTION hstore_in(cstring)
+RETURNS hstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_out(hstore)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_recv(internal)
+RETURNS hstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_send(hstore)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE hstore (
+        INTERNALLENGTH = -1,
+        INPUT = hstore_in,
+        OUTPUT = hstore_out,
+        RECEIVE = hstore_recv,
+        SEND = hstore_send,
+        STORAGE = extended
+);
+
+CREATE FUNCTION hstore_version_diag(hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_version_diag'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION fetchval(hstore,text)
+RETURNS text
+AS 'MODULE_PATHNAME','hstore_fetchval'
+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_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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR -> (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = slice_array
+);
+
+CREATE FUNCTION slice(hstore,text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_slice_to_hstore'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION isexists(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION exist(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ? (
+	LEFTARG = hstore,
+	RIGHTARG = text,
+	PROCEDURE = exist,
+	RESTRICT = contsel,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ?| (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = exists_any,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION exists_all(hstore,text[])
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_exists_all'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR ?& (
+	LEFTARG = hstore,
+	RIGHTARG = text[],
+	PROCEDURE = exists_all,
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION isdefined(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_defined'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION defined(hstore,text)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_defined'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,text)
+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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION delete(hstore,hstore)
+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[],
+	PROCEDURE = delete
+);
+
+CREATE OPERATOR - (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR || (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	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'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hs_contained(hstore,hstore)
+RETURNS bool
+AS 'MODULE_PATHNAME','hstore_contained'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR @> (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contains,
+	COMMUTATOR = '<@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contained,
+	COMMUTATOR = '@>',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+-- obsolete:
+CREATE OPERATOR @ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contains,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+	LEFTARG = hstore,
+	RIGHTARG = hstore,
+	PROCEDURE = hs_contained,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION tconvert(text,text)
+RETURNS hstore
+AS 'MODULE_PATHNAME','hstore_from_text'
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL)
+
+CREATE FUNCTION hstore(text,text)
+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'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null)
+
+CREATE FUNCTION hstore(text[])
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_array'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (text[] AS hstore)
+  WITH FUNCTION hstore(text[]);
+
+CREATE FUNCTION hstore_to_json(hstore)
+RETURNS json
+AS 'MODULE_PATHNAME', 'hstore_to_json'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS json)
+  WITH FUNCTION hstore_to_json(hstore);
+
+CREATE FUNCTION hstore2jsonb(hstore)
+RETURNS jsonb
+AS 'MODULE_PATHNAME', 'hstore2jsonb'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (hstore AS jsonb)
+  WITH FUNCTION hstore2jsonb(hstore);
+
+CREATE FUNCTION jsonb2hstore(jsonb)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'jsonb2hstore'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE CAST (jsonb AS hstore)
+  WITH FUNCTION jsonb2hstore(jsonb);
+
+CREATE FUNCTION hstore_to_json_loose(hstore)
+RETURNS json
+AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION hstore(record)
+RETURNS hstore
+AS 'MODULE_PATHNAME', 'hstore_from_record'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype)
+
+CREATE FUNCTION hstore_to_array(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_to_array'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR %% (
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_to_array
+);
+
+CREATE FUNCTION hstore_to_matrix(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_to_matrix'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR %# (
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_to_matrix
+);
+
+CREATE FUNCTION akeys(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_akeys'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION avals(hstore)
+RETURNS text[]
+AS 'MODULE_PATHNAME','hstore_avals'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION skeys(hstore)
+RETURNS setof text
+AS 'MODULE_PATHNAME','hstore_skeys'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION svals(hstore)
+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)
+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'
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore)
+
+CREATE OPERATOR #= (
+	LEFTARG = anyelement,
+	RIGHTARG = hstore,
+	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)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_eq'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_ne(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_ne'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_gt(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_gt'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_ge(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_ge'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_lt(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_lt'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_le(hstore,hstore)
+RETURNS boolean
+AS 'MODULE_PATHNAME','hstore_le'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION hstore_cmp(hstore,hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_cmp'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR = (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_eq,
+       COMMUTATOR = =,
+       NEGATOR = <>,
+       RESTRICT = eqsel,
+       JOIN = eqjoinsel,
+       MERGES,
+       HASHES
+);
+CREATE OPERATOR <> (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_ne,
+       COMMUTATOR = <>,
+       NEGATOR = =,
+       RESTRICT = neqsel,
+       JOIN = neqjoinsel
+);
+
+-- the comparison operators have funky names (and are undocumented)
+-- in an attempt to discourage anyone from actually using them. they
+-- only exist to support the btree opclass
+
+CREATE OPERATOR #<# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_lt,
+       COMMUTATOR = #>#,
+       NEGATOR = #>=#,
+       RESTRICT = scalarltsel,
+       JOIN = scalarltjoinsel
+);
+CREATE OPERATOR #<=# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_le,
+       COMMUTATOR = #>=#,
+       NEGATOR = #>#,
+       RESTRICT = scalarltsel,
+       JOIN = scalarltjoinsel
+);
+CREATE OPERATOR #># (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_gt,
+       COMMUTATOR = #<#,
+       NEGATOR = #<=#,
+       RESTRICT = scalargtsel,
+       JOIN = scalargtjoinsel
+);
+CREATE OPERATOR #>=# (
+       LEFTARG = hstore,
+       RIGHTARG = hstore,
+       PROCEDURE = hstore_ge,
+       COMMUTATOR = #<=#,
+       NEGATOR = #<#,
+       RESTRICT = scalargtsel,
+       JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR CLASS btree_hstore_ops
+DEFAULT FOR TYPE hstore USING btree
+AS
+	OPERATOR	1	#<# ,
+	OPERATOR	2	#<=# ,
+	OPERATOR	3	= ,
+	OPERATOR	4	#>=# ,
+	OPERATOR	5	#># ,
+	FUNCTION	1	hstore_cmp(hstore,hstore);
+
+-- hash support
+
+CREATE FUNCTION hstore_hash(hstore)
+RETURNS integer
+AS 'MODULE_PATHNAME','hstore_hash'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OPERATOR CLASS hash_hstore_ops
+DEFAULT FOR TYPE hstore USING hash
+AS
+	OPERATOR	1	= ,
+	FUNCTION	1	hstore_hash(hstore);
+
+-- GiST support
+
+CREATE TYPE ghstore;
+
+CREATE FUNCTION ghstore_in(cstring)
+RETURNS ghstore
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION ghstore_out(ghstore)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE ghstore (
+        INTERNALLENGTH = -1,
+        INPUT = ghstore_in,
+        OUTPUT = ghstore_out
+);
+
+CREATE FUNCTION ghstore_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_union(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_same(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR CLASS gist_hstore_ops
+DEFAULT FOR TYPE hstore USING gist
+AS
+	OPERATOR        7       @> ,
+	OPERATOR        9       ?(hstore,text) ,
+	OPERATOR        10      ?|(hstore,text[]) ,
+	OPERATOR        11      ?&(hstore,text[]) ,
+        --OPERATOR        8       <@ ,
+        OPERATOR        13      @ ,
+        --OPERATOR        14      ~ ,
+        FUNCTION        1       ghstore_consistent (internal, internal, int, oid, internal),
+        FUNCTION        2       ghstore_union (internal, internal),
+        FUNCTION        3       ghstore_compress (internal),
+        FUNCTION        4       ghstore_decompress (internal),
+        FUNCTION        5       ghstore_penalty (internal, internal, internal),
+        FUNCTION        6       ghstore_picksplit (internal, internal),
+        FUNCTION        7       ghstore_same (internal, internal, internal),
+        STORAGE         ghstore;
+
+-- GIN support: default opclass
+
+CREATE FUNCTION gin_extract_hstore(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR CLASS gin_hstore_ops
+DEFAULT FOR TYPE hstore USING gin
+AS
+	OPERATOR        7       @>,
+	OPERATOR        9       ?(hstore,text),
+	OPERATOR        10      ?|(hstore,text[]),
+	OPERATOR        11      ?&(hstore,text[]),
+	FUNCTION        1       bttextcmp(text,text),
+	FUNCTION        2       gin_extract_hstore(internal, internal),
+	FUNCTION        3       gin_extract_hstore_query(internal, internal, int2, internal, internal),
+	FUNCTION        4       gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
+	STORAGE         text;
+
+-- 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;
+
+
+
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index 9daf5e2..dcc3b68 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,5 +1,5 @@
 # hstore extension
 comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/hstore'
 relocatable = true
diff --git a/contrib/hstore/hstore.h b/contrib/hstore/hstore.h
index 23c8a6f..a66b435 100644
--- a/contrib/hstore/hstore.h
+++ b/contrib/hstore/hstore.h
@@ -4,9 +4,7 @@
 #ifndef __HSTORE_H__
 #define __HSTORE_H__
 
-#include "fmgr.h"
-#include "utils/array.h"
-
+#include "utils/jsonb.h"
 
 /*
  * HEntry: there is one of these for each key _and_ value in an hstore
@@ -15,158 +13,128 @@
  * by subtraction from the previous entry.	the ISFIRST flag lets us tell
  * whether there is a previous entry.
  */
-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]))
+
+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_ISCALAR is only used in send/recv */
+#define HENTRY_ISHASH		JENTRY_ISOBJECT
+#define HENTRY_ISARRAY		JENTRY_ISARRAY
+#define HENTRY_ISCALAR		JENTRY_ISCALAR
+
+#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_)
 
 /*
- * 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
+ * determined by the size of "endpos" (ie HENTRY_POSMASK)
  */
-#define HSTORE_MAX_KEY_LEN 0x3FFFFFFF
-#define HSTORE_MAX_VALUE_LEN 0x3FFFFFFF
+#define HSTORE_MAX_KEY_LEN 		HENTRY_POSMASK
+#define HSTORE_MAX_VALUE_LEN 	HENTRY_POSMASK
 
-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;
+typedef Jsonb HStore;
 
 /*
  * it's not possible to get more than 2^28 items into an hstore,
  * so we reserve the top few bits of the size field. See hstore_compat.c
  * for one reason why.	Some bits are left for future use here.
  */
-#define HS_FLAG_NEWVERSION 0x80000000
+#define HS_FLAG_NEWVERSION 		0x80000000
+#define HS_FLAG_ARRAY			JB_FLAG_ARRAY
+#define HS_FLAG_HASH			JB_FLAG_OBJECT
+#define HS_FLAG_SCALAR			JB_FLAG_SCALAR
 
-#define HS_COUNT(hsp_) ((hsp_)->size_ & 0x0FFFFFFF)
-#define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION)
+#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 HSHRDSIZE	(sizeof(HStore))
-#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) )
+/* DatumGetHStoreP includes support for reading old-format hstore values */
+extern HStore *hstoreUpgrade(Datum orig);
 
-/* note multiple evaluations of x */
-#define ARRPTR(x)		( (HEntry*) ( (HStore*)(x) + 1 ) )
-#define STRPTR(x)		( (char*)(ARRPTR(x) + HS_COUNT((HStore*)(x)) * 2) )
+#define DatumGetHStoreP(d) hstoreUpgrade(d)
 
-/* 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 PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x))
 
-/*
- * 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.
- */
+typedef JsonbPair HStorePair;
+typedef JsonbValue HStoreValue;
 
-/*
- * 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)
+/* JsonbValue.type renaming */
+#define hsvNull		jbvNull
+#define hsvString	jbvString
+#define hsvNumeric	jbvNumeric
+#define hsvBool		jbvBool
+#define hsvArray	jbvArray
+#define hsvHash		jbvHash
+#define hsvBinary	jbvBinary
 
 /*
- * add one key/item pair, from a Pairs structure, into an
- * under-construction hstore
+ * hstore support functions, they are mostly the same as jsonb
  */
-#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)
 
-/* DatumGetHStoreP includes support for reading old-format hstore values */
-extern HStore *hstoreUpgrade(Datum orig);
+#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 DatumGetHStoreP(d) hstoreUpgrade(d)
+#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))
 
-#define PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x))
+#define compareHStoreBinaryValue(a, b)				compareJsonbBinaryValue((a), (b))
+#define compareHStoreValue(a, b)					compareJsonbValue((a), (b))
 
+#define findUncompressedHStoreValueByValue(buffer, flags, lowbound, key)	\
+	findUncompressedJsonbValueByValue((buffer), (flags), (lowbound), (key))
+#define findUncompressedHStoreValue(buffer, flags, lowbound, key, keylen)	\
+	findUncompressedJsonbValue((buffer), (flags), (lowbound), (key), (keylen))
 
-/*
- * 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;
+#define getHStoreValue(buffer, flags, i)			getJsonbValue((buffer), (flags), (i))
+
+typedef ToJsonbState ToHStoreState;
+#define pushHStoreValue(state, r /* WHS_* */, v)	pushJsonbValue((state), (r), (v))
+
+extern bool stringIsNumber(char *string, int len, bool jsonNumber);
+
+extern uint32 compressHStore(HStoreValue *v, char *buffer);
+
+typedef JsonbIterator HStoreIterator;
+
+#define	HStoreIteratorInit(buffer)					JsonbIteratorInit(buffer)
 
-extern int	hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen);
-extern HStore *hstorePairs(Pairs *pairs, int32 pcount, int32 buflen);
+#define HStoreIteratorGet(it, v, skipNested)	JsonbIteratorGet((it), (v), (skipNested))
 
-extern size_t hstoreCheckKeyLen(size_t len);
-extern size_t hstoreCheckValLen(size_t len);
+text* HStoreValueToText(HStoreValue *v);
 
-extern int	hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen);
-extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs);
+extern HStoreValue* parseHStore(const char *str, int len, bool json);
+
+#define uniqueHStoreValue(v) uniqueJsonbValue(v)
 
 #define HStoreContainsStrategyNumber	7
 #define HStoreExistsStrategyNumber		9
@@ -194,4 +162,18 @@ extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs);
 	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__ */
diff --git a/contrib/hstore/hstore_compat.c b/contrib/hstore/hstore_compat.c
index 6327a8e..0e18505 100644
--- a/contrib/hstore/hstore_compat.c
+++ b/contrib/hstore/hstore_compat.c
@@ -105,9 +105,41 @@ typedef struct
 				pos:31;
 } HOldEntry;
 
-static int	hstoreValidNewFormat(HStore *hs);
-static int	hstoreValidOldFormat(HStore *hs);
+/*
+ * 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))
 
+#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_)]))
+
+/* 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.
@@ -116,7 +148,7 @@ static int	hstoreValidOldFormat(HStore *hs);
  *	2 = exactly valid
  */
 static int
-hstoreValidNewFormat(HStore *hs)
+hstoreValidNewFormat(HStoreV2 *hs)
 {
 	int			count = HS_COUNT(hs);
 	HEntry	   *entries = ARRPTR(hs);
@@ -168,7 +200,7 @@ hstoreValidNewFormat(HStore *hs)
  *	2 = exactly valid
  */
 static int
-hstoreValidOldFormat(HStore *hs)
+hstoreValidOldFormat(HStoreV2 *hs)
 {
 	int			count = hs->size_;
 	HOldEntry  *entries = (HOldEntry *) ARRPTR(hs);
@@ -235,16 +267,26 @@ hstoreValidOldFormat(HStore *hs)
 HStore *
 hstoreUpgrade(Datum orig)
 {
-	HStore	   *hs = (HStore *) PG_DETOAST_DATUM(orig);
+	HStoreV2	   *hs = (HStoreV2 *) PG_DETOAST_DATUM(orig);
 	int			valid_new;
 	int			valid_old;
 	bool		writable;
 
 	/* Return immediately if no conversion needed */
-	if ((hs->size_ & HS_FLAG_NEWVERSION) ||
+	if (VARSIZE_ANY(hs) <= VARHDRSZ ||
+		(hs->size_ & HS_FLAG_NEWVERSION) ||
 		hs->size_ == 0 ||
 		(VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0]))))
-		return hs;
+	{
+		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;
+	}
 
 	valid_new = hstoreValidNewFormat(hs);
 	valid_old = hstoreValidOldFormat(hs);
@@ -266,7 +308,7 @@ hstoreUpgrade(Datum orig)
 				HS_SETCOUNT(hs, HS_COUNT(hs));
 				HS_FIXSIZE(hs, HS_COUNT(hs));
 			}
-			return hs;
+			return (HStore*)hs;
 		}
 		else
 		{
@@ -323,7 +365,7 @@ hstoreUpgrade(Datum orig)
 	 */
 
 	if (!writable)
-		hs = (HStore *) PG_DETOAST_DATUM_COPY(orig);
+		hs = (HStoreV2 *) PG_DETOAST_DATUM_COPY(orig);
 
 	{
 		int			count = hs->size_;
@@ -352,7 +394,7 @@ hstoreUpgrade(Datum orig)
 		HS_FIXSIZE(hs, count);
 	}
 
-	return hs;
+	return (HStore*)hs;
 }
 
 
@@ -361,7 +403,7 @@ Datum		hstore_version_diag(PG_FUNCTION_ARGS);
 Datum
 hstore_version_diag(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = (HStore *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+	HStoreV2	   *hs = (HStoreV2 *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
 	int			valid_new = hstoreValidNewFormat(hs);
 	int			valid_old = hstoreValidOldFormat(hs);
 
diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c
index 2007801..ac77e2e 100644
--- a/contrib/hstore/hstore_gin.c
+++ b/contrib/hstore/hstore_gin.c
@@ -6,21 +6,11 @@
 #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);
 
@@ -41,37 +31,82 @@ 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:
+			cstr = DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric)));
+			item = makeitem(cstr, strlen(cstr), flag);
+			break;
+		case hsvString:
+			item = makeitem(v->string.val, v->string.len, flag);
+			break;
+		default:
+			elog(ERROR, "Wrong hstore 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;
-	HEntry	   *hsent = ARRPTR(hs);
-	char	   *ptr = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			i;
+	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;
 
-	*nentries = 2 * count;
-	if (count)
-		entries = (Datum *) palloc(sizeof(Datum) * 2 * count);
-
-	for (i = 0; i < count; ++i)
+	if (total == 0)
 	{
-		text	   *item;
+		*nentries = 0;
+		PG_RETURN_POINTER(NULL);
+	}
 
-		item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i),
-						KEYFLAG);
-		entries[2 * i] = PointerGetDatum(item);
+	entries = (Datum *) palloc(sizeof(Datum) * total);
 
-		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);
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, false)) != 0)
+	{
+		if (i >= total)
+		{
+			total *= 2;
+			entries = (Datum *) repalloc(entries, sizeof(Datum) * total);
+		}
+
+		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;
+		}
 	}
 
+	*nentries = i;
+
 	PG_RETURN_POINTER(entries);
 }
 
@@ -129,7 +164,8 @@ 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);
 		}
 
@@ -211,3 +247,196 @@ 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:
+			COMP_CRC32(stack->hash_state,
+					   VARDATA_ANY(v->numeric), VARSIZE_ANY_EXHDR(v->numeric));
+			break;
+		case hsvString:
+			COMP_CRC32(stack->hash_state, v->string.val, v->string.len);
+			break;
+		default:
+			elog(ERROR, "Shouldn't take hash of array");
+			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);
+}
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index 9001180..05b3f0a 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -6,8 +6,8 @@
 #include "access/gist.h"
 #include "access/skey.h"
 #include "catalog/pg_type.h"
+#include "utils/pg_crc.h"
 
-#include "crc32.h"
 #include "hstore.h"
 
 /* bigint defines */
@@ -105,6 +105,66 @@ 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:
+			COMP_CRC32(crc, VARDATA_ANY(v->numeric), VARSIZE_ANY_EXHDR(v->numeric));
+			break;
+		default:
+			elog(PANIC, "impossible value %d", v->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)
 {
@@ -113,25 +173,26 @@ ghstore_compress(PG_FUNCTION_ARGS)
 
 	if (entry->leafkey)
 	{
-		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;
+		GISTTYPE   		*res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+		HStore	   		*val = DatumGetHStoreP(entry->key);
 
 		SET_VARSIZE(res, CALCGTSIZE(0));
 
-		for (i = 0; i < count; ++i)
+		if (!HS_ISEMPTY(val))
 		{
-			int			h;
+			int				r;
+			HStoreIterator	*it = HStoreIteratorInit(VARDATA(val));
+			HStoreValue		v;
 
-			h = crc32_sz((char *) HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i));
-			HASH(GETSIGN(res), h);
-			if (!HS_VALISNULL(hsent, i))
+			while((r = HStoreIteratorGet(&it, &v, false)) != 0)
 			{
-				h = crc32_sz((char *) HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i));
-				HASH(GETSIGN(res), h);
+				if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) &&
+					v.type != hsvNull)
+				{
+					int   h = crc32_HStoreValue(&v, r);
+
+					HASH(GETSIGN(res), h);
+				}
 			}
 		}
 
@@ -396,7 +457,8 @@ 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)))
@@ -410,7 +472,8 @@ 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);
@@ -490,7 +553,6 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	PG_RETURN_POINTER(v);
 }
 
-
 Datum
 ghstore_consistent(PG_FUNCTION_ARGS)
 {
@@ -513,82 +575,123 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 	if (strategy == HStoreContainsStrategyNumber ||
 		strategy == HStoreOldContainsStrategyNumber)
 	{
-		HStore	   *query = PG_GETARG_HS(1);
-		HEntry	   *qe = ARRPTR(query);
-		char	   *qv = STRPTR(query);
-		int			count = HS_COUNT(query);
+		BITVECP		qe;
 		int			i;
 
-		for (i = 0; res && i < count; ++i)
+		qe = fcinfo->flinfo->fn_extra;
+		if (qe == NULL)
 		{
-			int			crc = crc32_sz((char *) HS_KEY(qe, qv, i), HS_KEYLEN(qe, i));
+			HStore	   		*query = PG_GETARG_HS(1);
+
+			qe = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(BITVEC));
+			memset(qe, 0, sizeof(BITVEC));
 
-			if (GETBIT(sign, HASHVAL(crc)))
+			if (!HS_ISEMPTY(query))
 			{
-				if (!HS_VALISNULL(qe, i))
+				int				r;
+				HStoreIterator	*it = HStoreIteratorInit(VARDATA(query));
+				HStoreValue		v;
+
+				while((r = HStoreIteratorGet(&it, &v, false)) != 0)
 				{
-					crc = crc32_sz((char *) HS_VAL(qe, qv, i), HS_VALLEN(qe, i));
-					if (!GETBIT(sign, HASHVAL(crc)))
-						res = false;
+					if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) && v.type != hsvNull)
+					{
+						int   crc = crc32_HStoreValue(&v, r);
+
+						HASH(qe, crc);
+					}
 				}
 			}
-			else
+
+			fcinfo->flinfo->fn_extra = qe;
+		}
+
+		LOOPBYTE
+		{
+			if ((sign[i] & qe[i]) != qe[i])
+			{
 				res = false;
+				break;
+			}
 		}
 	}
 	else if (strategy == HStoreExistsStrategyNumber)
 	{
-		text	   *query = PG_GETARG_TEXT_PP(1);
-		int			crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
+		int 	*qval;
 
-		res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
-	}
-	else if (strategy == HStoreExistsAllStrategyNumber)
-	{
-		ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
-		Datum	   *key_datums;
-		bool	   *key_nulls;
-		int			key_count;
-		int			i;
-
-		deconstruct_array(query,
-						  TEXTOID, -1, false, 'i',
-						  &key_datums, &key_nulls, &key_count);
-
-		for (i = 0; res && i < key_count; ++i)
+		qval = fcinfo->flinfo->fn_extra;
+		if (qval == NULL)
 		{
-			int			crc;
+			text	   *query = PG_GETARG_TEXT_PP(1);
+			int			crc = crc32_Key(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
 
-			if (key_nulls[i])
-				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (!(GETBIT(sign, HASHVAL(crc))))
-				res = FALSE;
+			qval = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(*qval));
+			*qval = HASHVAL(crc);
+
+			fcinfo->flinfo->fn_extra = qval;
 		}
+
+		res = (GETBIT(sign, *qval)) ? true : false;
 	}
-	else if (strategy == HStoreExistsAnyStrategyNumber)
+	else if (strategy == HStoreExistsAllStrategyNumber ||
+			 strategy == HStoreExistsAnyStrategyNumber)
 	{
-		ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
-		Datum	   *key_datums;
-		bool	   *key_nulls;
-		int			key_count;
-		int			i;
+		BITVECP	arrentry;
+		int		i;
 
-		deconstruct_array(query,
-						  TEXTOID, -1, false, 'i',
-						  &key_datums, &key_nulls, &key_count);
+		arrentry = fcinfo->flinfo->fn_extra;
+		if (arrentry == NULL)
+		{
+			ArrayType  *query = PG_GETARG_ARRAYTYPE_P(1);
+			Datum	   *key_datums;
+			bool	   *key_nulls;
+			int			key_count;
 
-		res = FALSE;
+			arrentry = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+										  sizeof(BITVEC));
+			memset(arrentry, 0, sizeof(BITVEC));
 
-		for (i = 0; !res && i < key_count; ++i)
+			deconstruct_array(query,
+							  TEXTOID, -1, false, 'i',
+							  &key_datums, &key_nulls, &key_count);
+
+			for (i = 0; i < key_count; ++i)
+			{
+				int			crc;
+
+				if (key_nulls[i])
+					continue;
+				crc = crc32_Key(VARDATA(key_datums[i]),
+								VARSIZE(key_datums[i]) - VARHDRSZ);
+				HASH(arrentry, crc);
+			}
+
+			fcinfo->flinfo->fn_extra = arrentry;
+		}
+
+		if (strategy == HStoreExistsAllStrategyNumber)
 		{
-			int			crc;
+			LOOPBYTE
+			{
+				if ((sign[i] & arrentry[i]) != arrentry[i])
+				{
+					res = false;
+					break;
+				}
+			}
+		}
+		else /* HStoreExistsAnyStrategyNumber */
+		{
+			res = false;
 
-			if (key_nulls[i])
-				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (GETBIT(sign, HASHVAL(crc)))
-				res = TRUE;
+			LOOPBYTE
+			{
+				if (sign[i] & arrentry[i])
+				{
+					res = true;
+					break;
+				}
+			}
 		}
 	}
 	else
diff --git a/contrib/hstore/hstore_gram.y b/contrib/hstore/hstore_gram.y
new file mode 100644
index 0000000..d0883fa
--- /dev/null
+++ b/contrib/hstore/hstore_gram.y
@@ -0,0 +1,341 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 973a126..3bfbe56 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -7,12 +7,16 @@
 
 #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/syscache.h"
 #include "utils/typcache.h"
 
 #include "hstore.h"
@@ -22,507 +26,454 @@ 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))
 
-typedef struct
+static void recvHStore(StringInfo buf, HStoreValue *v, uint32 level,
+					   uint32 header);
+static Oid searchCast(Oid src, Oid dst, CoercionMethod *method);
+
+typedef enum HStoreOutputKind {
+	JsonOutput = 0x01,
+	LooseOutput = 0x02,
+	ArrayCurlyBraces = 0x04,
+	RootHashDecorated = 0x08,
+	PrettyPrint = 0x10
+} HStoreOutputKind;
+
+static char* HStoreToCString(StringInfo out, char *in,
+							 int len /* just estimation */, HStoreOutputKind kind);
+
+static size_t
+hstoreCheckKeyLen(size_t len)
 {
-	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
+	if (len > HSTORE_MAX_KEY_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for hstore key")));
+	return len;
+}
 
-static bool
-get_val(HSParser *state, bool ignoreeq, bool *escaped)
+static size_t
+hstoreCheckValLen(size_t len)
 {
-	int			st = GV_WAITVAL;
+	if (len > HSTORE_MAX_VALUE_LEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+				 errmsg("string too long for hstore value")));
+	return len;
+}
 
-	state->wordlen = 32;
-	state->cur = state->word = palloc(state->wordlen);
-	*escaped = false;
 
-	while (1)
+static HStore*
+hstoreDump(HStoreValue *p)
+{
+	uint32			buflen;
+	HStore	 	   *out;
+
+	if (p == NULL)
 	{
-		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__);
+		buflen = 0;
+		out = palloc(VARHDRSZ);
+	}
+	else
+	{
+		buflen = VARHDRSZ + p->size;
+		out = palloc(buflen);
+		SET_VARSIZE(out, buflen);
 
-		state->ptr++;
+		buflen = compressHStore(p, VARDATA(out));
 	}
+	SET_VARSIZE(out, buflen + VARHDRSZ);
+
+	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)));
 }
 
-#define WKEY	0
-#define WVAL	1
-#define WEQ 2
-#define WGT 3
-#define WDEL	4
+static void
+recvHStoreValue(StringInfo buf, HStoreValue *v, uint32 level, int c)
+{
+	uint32  hentry = c & HENTRY_TYPEMASK;
 
+	if (c == -1 /* compatibility */ || hentry == HENTRY_ISNULL)
+	{
+		v->type = hsvNull;
+		v->size = sizeof(HEntry);
+	}
+	else if (hentry == HENTRY_ISHASH || hentry == HENTRY_ISARRAY ||
+			 hentry == HENTRY_ISCALAR)
+	{
+		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, &c);
+		v->string.len = hstoreCheckKeyLen(c);
+		v->size = sizeof(HEntry) + v->string.len;
+	}
+	else
+	{
+		elog(ERROR, "bogus input");
+	}
+}
 
 static void
-parse_hstore(HSParser *state)
+recvHStore(StringInfo buf, HStoreValue *v, uint32 level, uint32 header)
 {
-	int			st = WKEY;
-	bool		escaped = false;
+	uint32	hentry;
+	uint32	i;
 
-	state->plen = 16;
-	state->pairs = (Pairs *) palloc(sizeof(Pairs) * state->plen);
-	state->pcur = 0;
-	state->ptr = state->begin;
-	state->word = NULL;
+	hentry = header & HENTRY_TYPEMASK;
 
-	while (1)
+	if (level == 0 && hentry == 0)
+		hentry = HENTRY_ISHASH; /* old version */
+
+	v->size = 3 * sizeof(HEntry);
+	if (hentry == HENTRY_ISHASH)
 	{
-		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)
+		v->type = hsvHash;
+		v->hash.npairs = header & HS_COUNT_MASK;
+		if (v->hash.npairs > 0)
 		{
-			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)
+			v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+			for(i=0; i<v->hash.npairs; i++)
 			{
-				state->word[4] = '\0';
-				if (0 == pg_strcasecmp(state->word, "null"))
-					state->pairs[state->pcur].isnull = true;
+				recvHStoreValue(buf, &v->hash.pairs[i].key, level,
+								pq_getmsgint(buf, 4));
+				if (v->hash.pairs[i].key.type != hsvString)
+					elog(ERROR, "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;
 			}
-			state->word = NULL;
-			state->pcur++;
-			st = WDEL;
+
+			uniqueHStoreValue(v);
 		}
-		else if (st == WDEL)
+	}
+	else if (hentry == HENTRY_ISARRAY || hentry == HENTRY_ISCALAR)
+	{
+		v->type = hsvArray;
+		v->array.nelems = header & HS_COUNT_MASK;
+		v->array.scalar = (hentry == HENTRY_ISCALAR) ? true : false;
+
+		if (v->array.scalar && v->array.nelems != 1)
+			elog(ERROR, "bogus input");
+
+		if (v->array.nelems > 0)
 		{
-			if (*(state->ptr) == ',')
-			{
-				st = WKEY;
-			}
-			else if (*(state->ptr) == '\0')
-			{
-				return;
-			}
-			else if (!isspace((unsigned char) *(state->ptr)))
+			v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+			for(i=0; i<v->array.nelems; i++)
 			{
-				elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
+				recvHStoreValue(buf, v->array.elems + i, level,
+								pq_getmsgint(buf, 4));
+				v->size += v->array.elems[i].size;
 			}
 		}
-		else
-			elog(ERROR, "Unknown state %d at line %d in file '%s'", st, __LINE__, __FILE__);
-
-		state->ptr++;
+	}
+	else
+	{
+			elog(ERROR, "bogus input");
 	}
 }
 
-static int
-comparePairs(const void *a, const void *b)
+PG_FUNCTION_INFO_V1(hstore_recv);
+Datum		hstore_recv(PG_FUNCTION_ARGS);
+Datum
+hstore_recv(PG_FUNCTION_ARGS)
 {
-	const Pairs *pa = a;
-	const Pairs *pb = b;
-
-	if (pa->keylen == pb->keylen)
-	{
-		int			res = memcmp(pa->key, pb->key, pa->keylen);
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	HStoreValue	v;
 
-		if (res)
-			return res;
+	recvHStore(buf, &v, 0, pq_getmsgint(buf, 4));
 
-		/* 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;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-/*
- * 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)
+PG_FUNCTION_INFO_V1(hstore_from_text);
+Datum		hstore_from_text(PG_FUNCTION_ARGS);
+Datum
+hstore_from_text(PG_FUNCTION_ARGS)
 {
-	Pairs	   *ptr,
-			   *res;
+	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);
 
-	*buflen = 0;
-	if (l < 2)
+	if (PG_ARGISNULL(1))
 	{
-		if (l == 1)
-			*buflen = a->keylen + ((a->isnull) ? 0 : a->vallen);
-		return l;
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
 	}
-
-	qsort((void *) a, l, sizeof(Pairs), comparePairs);
-	ptr = a + 1;
-	res = a;
-	while (ptr - a < l)
+	else
 	{
-		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));
-		}
+		text	   	*val = NULL;
 
-		ptr++;
+		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);
 	}
 
-	*buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
-	return res + 1 - a;
-}
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-size_t
-hstoreCheckKeyLen(size_t len)
-{
-	if (len > HSTORE_MAX_KEY_LEN)
-		ereport(ERROR,
-				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
-				 errmsg("string too long for hstore key")));
-	return len;
-}
-
-size_t
-hstoreCheckValLen(size_t len)
-{
-	if (len > HSTORE_MAX_VALUE_LEN)
-		ereport(ERROR,
-				(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
-				 errmsg("string too long for hstore value")));
-	return len;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-HStore *
-hstorePairs(Pairs *pairs, int32 pcount, int32 buflen)
+PG_FUNCTION_INFO_V1(hstore_from_bool);
+Datum		hstore_from_bool(PG_FUNCTION_ARGS);
+Datum
+hstore_from_bool(PG_FUNCTION_ARGS)
 {
-	HStore	   *out;
-	HEntry	   *entry;
-	char	   *ptr;
-	char	   *buf;
-	int32		len;
-	int32		i;
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	len = CALCDATASIZE(pcount, buflen);
-	out = palloc(len);
-	SET_VARSIZE(out, len);
-	HS_SETCOUNT(out, pcount);
-
-	if (pcount == 0)
-		return out;
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	entry = ARRPTR(out);
-	buf = ptr = STRPTR(out);
+	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);
 
-	for (i = 0; i < pcount; i++)
-		HS_ADDITEM(entry, buf, ptr, pairs[i]);
+	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);
+	}
 
-	HS_FINALIZE(out, pcount, buf, ptr);
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-	return out;
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_in);
-Datum		hstore_in(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_from_numeric);
+Datum		hstore_from_numeric(PG_FUNCTION_ARGS);
 Datum
-hstore_in(PG_FUNCTION_ARGS)
+hstore_from_numeric(PG_FUNCTION_ARGS)
 {
-	HSParser	state;
-	int32		buflen;
-	HStore	   *out;
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	state.begin = PG_GETARG_CSTRING(0);
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	parse_hstore(&state);
+	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);
 
-	state.pcur = hstoreUniquePairs(state.pairs, state.pcur, &buflen);
+	if (PG_ARGISNULL(1))
+	{
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
+	}
+	else
+	{
+		pair.value.type = hsvNumeric;
+		pair.value.numeric = PG_GETARG_NUMERIC(1);
+		pair.value.size = sizeof(HEntry) + sizeof(HEntry) +
+							VARSIZE_ANY(pair.value.numeric);
+	}
 
-	out = hstorePairs(state.pairs, state.pcur, buflen);
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_recv);
-Datum		hstore_recv(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_from_th);
+Datum		hstore_from_th(PG_FUNCTION_ARGS);
 Datum
-hstore_recv(PG_FUNCTION_ARGS)
+hstore_from_th(PG_FUNCTION_ARGS)
 {
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
-	int32		i;
-	int32		pcount;
-	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	text	   	*key;
+	HStoreValue	v;
+	HStorePair	pair;
 
-	pcount = pq_getmsgint(buf, 4);
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
 
-	if (pcount == 0)
+	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))
 	{
-		out = hstorePairs(NULL, 0, 0);
-		PG_RETURN_POINTER(out);
+		pair.value.type = hsvNull;
+		pair.value.size = sizeof(HEntry);
 	}
+	else
+	{
+		HStore	   	*val = NULL;
 
-	pairs = palloc(pcount * sizeof(Pairs));
+		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;
+	}
 
-	for (i = 0; i < pcount; ++i)
-	{
-		int			rawlen = pq_getmsgint(buf, 4);
-		int			len;
+	v.type = hsvHash;
+	v.size = sizeof(HEntry) + pair.key.size + pair.value.size;
+	v.hash.npairs = 1;
+	v.hash.pairs = &pair;
 
-		if (rawlen < 0)
-			ereport(ERROR,
-					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-					 errmsg("null value not allowed for hstore key")));
+	PG_RETURN_POINTER(hstoreDump(&v));
+}
 
-		pairs[i].key = pq_getmsgtext(buf, rawlen, &len);
-		pairs[i].keylen = hstoreCheckKeyLen(len);
-		pairs[i].needfree = true;
+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;
 
-		rawlen = pq_getmsgint(buf, 4);
-		if (rawlen < 0)
-		{
-			pairs[i].val = NULL;
-			pairs[i].vallen = 0;
-			pairs[i].isnull = true;
-		}
-		else
-		{
-			pairs[i].val = pq_getmsgtext(buf, rawlen, &len);
-			pairs[i].vallen = hstoreCheckValLen(len);
-			pairs[i].isnull = false;
-		}
+	if (PG_ARGISNULL(0))
+	{
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
 	}
+	else
+	{
+		text	*scalar;
 
-	pcount = hstoreUniquePairs(pairs, pcount, &buflen);
+		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);
+	}
 
-	out = hstorePairs(pairs, pcount, buflen);
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&a));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_from_text);
-Datum		hstore_from_text(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_scalar_from_bool);
+Datum		hstore_scalar_from_bool(PG_FUNCTION_ARGS);
 Datum
-hstore_from_text(PG_FUNCTION_ARGS)
+hstore_scalar_from_bool(PG_FUNCTION_ARGS)
 {
-	text	   *key;
-	text	   *val = NULL;
-	Pairs		p;
-	HStore	   *out;
+	HStoreValue	a, v;
 
 	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
+	{
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
+	}
+	else
+	{
+		v.type = hsvBool;
+		v.boolean = PG_GETARG_BOOL(0);
+		v.size = sizeof(HEntry);
+	}
 
-	p.needfree = false;
-	key = PG_GETARG_TEXT_PP(0);
-	p.key = VARDATA_ANY(key);
-	p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	if (PG_ARGISNULL(1))
+	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))
 	{
-		p.vallen = 0;
-		p.isnull = true;
+		v.type = hsvNull;
+		v.size = sizeof(HEntry);
 	}
 	else
 	{
-		val = PG_GETARG_TEXT_PP(1);
-		p.val = VARDATA_ANY(val);
-		p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
-		p.isnull = false;
+		v.type = hsvNumeric;
+		v.numeric = PG_GETARG_NUMERIC(0);
+		v.size = VARSIZE_ANY(v.numeric) + 2*sizeof(HEntry);
 	}
 
-	out = hstorePairs(&p, 1, p.keylen + p.vallen);
+	a.type = hsvArray;
+	a.size = sizeof(HEntry) + v.size;
+	a.array.nelems = 1;
+	a.array.elems = &v;
+	a.array.scalar = true;
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&a));
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_from_arrays);
 Datum		hstore_from_arrays(PG_FUNCTION_ARGS);
 Datum
 hstore_from_arrays(PG_FUNCTION_ARGS)
 {
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
+	HStoreValue v;
 	Datum	   *key_datums;
 	bool	   *key_nulls;
 	int			key_count;
@@ -589,7 +540,10 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
 		Assert(key_count == value_count);
 	}
 
-	pairs = palloc(key_count * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2 * sizeof(HEntry);
+	v.hash.pairs = palloc(key_count * sizeof(*v.hash.pairs));
+	v.hash.npairs = key_count;
 
 	for (i = 0; i < key_count; ++i)
 	{
@@ -598,31 +552,34 @@ 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])
 		{
-			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;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
 		else
 		{
-			pairs[i].key = VARDATA_ANY(key_datums[i]);
-			pairs[i].val = VARDATA_ANY(value_datums[i]);
-			pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
-			pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i]));
-			pairs[i].isnull = false;
-			pairs[i].needfree = false;
+			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;
 		}
+
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
 	}
 
-	key_count = hstoreUniquePairs(pairs, key_count, &buflen);
+	uniqueHStoreValue(&v);
 
-	out = hstorePairs(pairs, key_count, buflen);
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
 
@@ -634,9 +591,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
 	ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
 	int			ndims = ARR_NDIM(in_array);
 	int			count;
-	int32		buflen;
-	HStore	   *out;
-	Pairs	   *pairs;
+	HStoreValue	v;
 	Datum	   *in_datums;
 	bool	   *in_nulls;
 	int			in_count;
@@ -647,8 +602,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
 	switch (ndims)
 	{
 		case 0:
-			out = hstorePairs(NULL, 0, 0);
-			PG_RETURN_POINTER(out);
+			PG_RETURN_POINTER(hstoreDump(NULL));
 
 		case 1:
 			if ((ARR_DIMS(in_array)[0]) % 2)
@@ -676,7 +630,10 @@ hstore_from_array(PG_FUNCTION_ARGS)
 
 	count = in_count / 2;
 
-	pairs = palloc(count * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2*sizeof(HEntry);
+	v.hash.npairs = count;
+	v.hash.pairs = palloc(count * sizeof(HStorePair));
 
 	for (i = 0; i < count; ++i)
 	{
@@ -685,31 +642,33 @@ 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])
 		{
-			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;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
 		else
 		{
-			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.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;
 		}
-	}
 
-	count = hstoreUniquePairs(pairs, count, &buflen);
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
+	}
 
-	out = hstorePairs(pairs, count, buflen);
+	uniqueHStoreValue(&v);
 
-	PG_RETURN_POINTER(out);
+	PG_RETURN_POINTER(hstoreDump(&v));
 }
 
 /* most of hstore_from_record is shamelessly swiped from record_out */
@@ -739,19 +698,17 @@ Datum
 hstore_from_record(PG_FUNCTION_ARGS)
 {
 	HeapTupleHeader rec;
-	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;
+	HStore		   *out;
+	HStoreValue	   v;
+	Oid				tupType;
+	int32			tupTypmod;
+	TupleDesc		tupdesc;
+	HeapTupleData 	tuple;
+	RecordIOData   *my_extra;
+	int				ncolumns;
+	int				i;
+	Datum	   	   *values;
+	bool	   	   *nulls;
 
 	if (PG_ARGISNULL(0))
 	{
@@ -807,7 +764,10 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		my_extra->ncolumns = ncolumns;
 	}
 
-	pairs = palloc(ncolumns * sizeof(Pairs));
+	v.type = hsvHash;
+	v.size = 2*sizeof(HEntry);
+	v.hash.npairs = ncolumns;
+	v.hash.pairs = palloc(ncolumns * sizeof(HStorePair));
 
 	if (rec)
 	{
@@ -829,7 +789,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		nulls = NULL;
 	}
 
-	for (i = 0, j = 0; i < ncolumns; ++i)
+	for (i = 0; i < ncolumns; ++i)
 	{
 		ColumnIOData *column_info = &my_extra->columns[i];
 		Oid			column_type = tupdesc->attrs[i]->atttypid;
@@ -839,46 +799,82 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		if (tupdesc->attrs[i]->attisdropped)
 			continue;
 
-		pairs[j].key = NameStr(tupdesc->attrs[i]->attname);
-		pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(tupdesc->attrs[i]->attname)));
+		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;
 
 		if (!nulls || nulls[i])
 		{
-			pairs[j].val = NULL;
-			pairs[j].vallen = 4;
-			pairs[j].isnull = true;
-			pairs[j].needfree = false;
-			++j;
-			continue;
+			v.hash.pairs[i].value.type = hsvNull;
+			v.hash.pairs[i].value.size = sizeof(HEntry);
 		}
-
-		/*
-		 * Convert the column value to text
-		 */
-		if (column_info->column_type != column_type)
+		else
 		{
-			bool		typIsVarlena;
+			/*
+			 * 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;
 
-			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.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]);
+				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;
+				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;
+			}
+		}
+
+		v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size;
 	}
 
-	ncolumns = hstoreUniquePairs(pairs, j, &buflen);
+	uniqueHStoreValue(&v);
 
-	out = hstorePairs(pairs, ncolumns, buflen);
+	out = hstoreDump(&v);
 
 	ReleaseTupleDesc(tupdesc);
 
@@ -893,8 +889,6 @@ 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;
@@ -940,8 +934,6 @@ 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
@@ -949,7 +941,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 	 * domain nulls.
 	 */
 
-	if (HS_COUNT(hs) == 0 && rec)
+	if (HS_ISEMPTY(hs) && rec)
 		PG_RETURN_POINTER(rec);
 
 	tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
@@ -1009,94 +1001,464 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 		}
 	}
 
-	for (i = 0; i < ncolumns; ++i)
+	for (i = 0; i < ncolumns; ++i)
+	{
+		ColumnIOData *column_info = &my_extra->columns[i];
+		Oid			column_type = tupdesc->attrs[i]->atttypid;
+		HStoreValue	*v = NULL;
+
+		/* Ignore dropped columns in datatype */
+		if (tupdesc->attrs[i]->attisdropped)
+		{
+			nulls[i] = true;
+			continue;
+		}
+
+		if (!HS_ISEMPTY(hs))
+		{
+			char *key = NameStr(tupdesc->attrs[i]->attname);
+
+			v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HASH, NULL, key, strlen(key));
+		}
+
+		/*
+		 * we can't just skip here if the key wasn't found since we might have
+		 * a domain to deal with. If we were passed in a non-null record
+		 * datum, we assume that the existing values are valid (if they're
+		 * not, then it's not our fault), but if we were passed in a null,
+		 * 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)
+			continue;
+
+		/*
+		 * Prepare to convert the column value from text
+		 */
+		if (column_info->column_type != column_type)
+		{
+			getTypeInputInfo(column_type,
+							 &column_info->typiofunc,
+							 &column_info->typioparam);
+			fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
+						  fcinfo->flinfo->fn_mcxt);
+			column_info->column_type = column_type;
+		}
+
+		if (v == NULL || v->type == hsvNull)
+		{
+			/*
+			 * need InputFunctionCall to happen even for nulls, so that domain
+			 * checks are done
+			 */
+			values[i] = InputFunctionCall(&column_info->proc, NULL,
+										  column_info->typioparam,
+										  tupdesc->attrs[i]->atttypmod);
+			nulls[i] = true;
+		}
+		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(PANIC, "Wrong hstore");
+
+			values[i] = InputFunctionCall(&column_info->proc, s,
+										  column_info->typioparam,
+										  tupdesc->attrs[i]->atttypmod);
+			nulls[i] = false;
+		}
+	}
+
+	rettuple = heap_form_tuple(tupdesc, values, nulls);
+
+	ReleaseTupleDesc(tupdesc);
+
+	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)
+{
+	if (kind & PrettyPrint)
+	{
+		int i;
+
+		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)
+	{
+		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);
+	}
+}
+
+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(PANIC, "Unknown type");
+	}
+}
+
+static bool
+needBrackets(int level, bool isArray, HStoreOutputKind kind, bool isScalar)
+{
+	bool res;
+
+	if (isArray && isScalar)
+		res = false;
+	else if (level == 0)
+		res = (isArray || (kind & RootHashDecorated)) ? true : false;
+	else
+		res = true;
+
+	return res;
+}
+
+static bool
+isArrayBrackets(HStoreOutputKind kind)
+{
+	return ((kind & ArrayCurlyBraces) == 0) ? true : false;
+}
+		
+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;
+
+	if (out == NULL)
+		out = makeStringInfo();
+
+	if (in == NULL)
 	{
-		ColumnIOData *column_info = &my_extra->columns[i];
-		Oid			column_type = tupdesc->attrs[i]->atttypid;
-		char	   *value;
-		int			idx;
-		int			vallen;
+		appendStringInfoString(out, "");
+		return out->data;
+	}
 
-		/* Ignore dropped columns in datatype */
-		if (tupdesc->attrs[i]->attisdropped)
+	enlargeStringInfo(out, (len >= 0) ? len : 64);
+
+	it = HStoreIteratorInit(in);
+
+	while((type = HStoreIteratorGet(&it, &v, false)) != 0)
+	{
+reout:
+		switch(type)
 		{
-			nulls[i] = true;
-			continue;
-		}
+			case WHS_BEGIN_ARRAY:
+				if (first == false)
+				{
+					appendBinaryStringInfo(out, ", ", 2);
+					printCR(out, kind);
+				}
+				first = true;
 
-		idx = hstoreFindKey(hs, 0,
-							NameStr(tupdesc->attrs[i]->attname),
-							strlen(NameStr(tupdesc->attrs[i]->attname)));
+				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;
 
-		/*
-		 * we can't just skip here if the key wasn't found since we might have
-		 * a domain to deal with. If we were passed in a non-null record
-		 * datum, we assume that the existing values are valid (if they're
-		 * not, then it's not our fault), but if we were passed in a null,
-		 * 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 (idx < 0 && rec)
-			continue;
+				if (level == 0)
+					isRootHash = true;
 
-		/*
-		 * Prepare to convert the column value from text
-		 */
-		if (column_info->column_type != column_type)
-		{
-			getTypeInputInfo(column_type,
-							 &column_info->typiofunc,
-							 &column_info->typioparam);
-			fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
-						  fcinfo->flinfo->fn_mcxt);
-			column_info->column_type = column_type;
-		}
+				if (needBrackets(level, false, kind, false))
+				{
+					printIndent(out, isRootHash, kind, level);
+					appendStringInfoCharMacro(out, '{');
+					printCR(out, kind);
+				}
 
-		if (idx < 0 || HS_VALISNULL(entries, idx))
-		{
-			/*
-			 * need InputFunctionCall to happen even for nulls, so that domain
-			 * checks are done
-			 */
-			values[i] = InputFunctionCall(&column_info->proc, NULL,
-										  column_info->typioparam,
-										  tupdesc->attrs[i]->atttypmod);
-			nulls[i] = true;
-		}
-		else
-		{
-			vallen = HS_VALLEN(entries, idx);
-			value = palloc(1 + vallen);
-			memcpy(value, HS_VAL(entries, ptr, idx), vallen);
-			value[vallen] = 0;
+				level++;
+				break;
+			case WHS_KEY:
+				if (first == false)
+				{
+					appendBinaryStringInfo(out, ", ", 2);
+					printCR(out, kind);
+				}
+				first = true;
 
-			values[i] = InputFunctionCall(&column_info->proc, value,
-										  column_info->typioparam,
-										  tupdesc->attrs[i]->atttypmod);
-			nulls[i] = false;
+				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(PANIC, "Wrong flags");
 		}
 	}
 
-	rettuple = heap_form_tuple(tupdesc, values, nulls);
-
-	ReleaseTupleDesc(tupdesc);
+	Assert(level == 0);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(rettuple));
+	return out->data;
 }
 
-
-static char *
-cpw(char *dst, char *src, int len)
+text*
+HStoreValueToText(HStoreValue *v)
 {
-	char	   *ptr = src;
+	text		*out;
 
-	while (ptr - src < len)
+	if (v == NULL || v->type == hsvNull)
 	{
-		if (*ptr == '"' || *ptr == '\\')
-			*dst++ = '\\';
-		*dst++ = *ptr++;
+		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))
+		));
 	}
-	return dst;
+	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);
+	}
+
+	return out;
 }
 
 PG_FUNCTION_INFO_V1(hstore_out);
@@ -1104,105 +1466,85 @@ Datum		hstore_out(PG_FUNCTION_ARGS);
 Datum
 hstore_out(PG_FUNCTION_ARGS)
 {
-	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);
-
-	if (count == 0)
-		PG_RETURN_CSTRING(pstrdup(""));
-
-	buflen = 0;
-
-	/*
-	 * this loop overestimates due to pessimistic assumptions about escaping,
-	 * so very large hstore values can't be output. this could be fixed, but
-	 * many other data types probably have the same issue. This replaced code
-	 * that used the original varlena size for calculations, which was wrong
-	 * in some subtle ways.
-	 */
-
-	for (i = 0; i < count; i++)
-	{
-		/* include "" and => and comma-space */
-		buflen += 6 + 2 * HS_KEYLEN(entries, i);
-		/* include "" only if nonnull */
-		buflen += 2 + (HS_VALISNULL(entries, i)
-					   ? 2
-					   : 2 * HS_VALLEN(entries, i));
-	}
-
-	out = ptr = palloc(buflen);
-
-	for (i = 0; i < count; i++)
-	{
-		*ptr++ = '"';
-		ptr = cpw(ptr, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		*ptr++ = '"';
-		*ptr++ = '=';
-		*ptr++ = '>';
-		if (HS_VALISNULL(entries, i))
-		{
-			*ptr++ = 'N';
-			*ptr++ = 'U';
-			*ptr++ = 'L';
-			*ptr++ = 'L';
-		}
-		else
-		{
-			*ptr++ = '"';
-			ptr = cpw(ptr, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
-			*ptr++ = '"';
-		}
+	HStore	*hs = PG_GETARG_HS(0);
+	char 	*out;
 
-		if (i + 1 != count)
-		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
-		}
-	}
-	*ptr = '\0';
+	out = HStoreToCString(NULL, (HS_ISEMPTY(hs)) ? NULL : VARDATA(hs), 
+						  VARSIZE(hs), SET_PRETTY_PRINT_VAR(0));
 
 	PG_RETURN_CSTRING(out);
 }
 
-
 PG_FUNCTION_INFO_V1(hstore_send);
 Datum		hstore_send(PG_FUNCTION_ARGS);
 Datum
 hstore_send(PG_FUNCTION_ARGS)
 {
-	HStore	   *in = PG_GETARG_HS(0);
-	int			i;
-	int			count = HS_COUNT(in);
-	char	   *base = STRPTR(in);
-	HEntry	   *entries = ARRPTR(in);
-	StringInfoData buf;
+	HStore	   		*in = PG_GETARG_HS(0);
+	StringInfoData	buf;
 
 	pq_begintypsend(&buf);
 
-	pq_sendint(&buf, count, 4);
-
-	for (i = 0; i < count; i++)
+	if (HS_ISEMPTY(in))
 	{
-		int32		keylen = HS_KEYLEN(entries, i);
+		pq_sendint(&buf, 0, 4);
+	}
+	else
+	{
+		HStoreIterator	*it;
+		int				type;
+		HStoreValue		v;
+		uint32			flag;
+		bytea			*nbuf;
 
-		pq_sendint(&buf, keylen, 4);
-		pq_sendtext(&buf, HS_KEY(entries, base, i), keylen);
-		if (HS_VALISNULL(entries, i))
-		{
-			pq_sendint(&buf, -1, 4);
-		}
-		else
-		{
-			int32		vallen = HS_VALLEN(entries, i);
+		enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */);
+
+		it = HStoreIteratorInit(VARDATA_ANY(in));
 
-			pq_sendint(&buf, vallen, 4);
-			pq_sendtext(&buf, HS_VAL(entries, base, i), vallen);
+		while((type = HStoreIteratorGet(&it, &v, false)) != 0)
+		{
+			switch(type)
+			{
+				case WHS_BEGIN_ARRAY:
+					flag = (v.array.scalar) ? HENTRY_ISCALAR : 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, VARSIZE_ANY(nbuf) | HENTRY_ISNUMERIC, 4);
+							pq_sendbytes(&buf, (char*)nbuf, VARSIZE_ANY(nbuf));
+							break;
+						default:
+							elog(PANIC, "Wrong type: %u", v.type);
+					}
+					break;
+				case WHS_END_ARRAY:
+				case WHS_END_HASH:
+					break;
+				default:
+					elog(PANIC, "Wrong flags");
+			}
 		}
 	}
 
@@ -1224,124 +1566,28 @@ Datum
 hstore_to_json_loose(PG_FUNCTION_ARGS)
 {
 	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);
-	bool		is_number;
-	StringInfo	src,
-				dst;
-
-	if (count == 0)
-		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
-
-	buflen = 3;
+	text	   *out;
 
-	/*
-	 * Formula adjusted slightly from the logic in hstore_out. We have to take
-	 * account of out treatment of booleans to be a bit more pessimistic about
-	 * the length of values.
-	 */
+	if (HS_ISEMPTY(in))
+	{
+		out = cstring_to_text_with_len("{}",2);
+	}
+	else
+	{
+		StringInfo	str;
 
-	for (i = 0; i < count; i++)
-	{
-		/* include "" and colon-space and comma-space */
-		buflen += 6 + 2 * HS_KEYLEN(entries, i);
-		/* include "" only if nonnull */
-		buflen += 3 + (HS_VALISNULL(entries, i)
-					   ? 1
-					   : 2 * HS_VALLEN(entries, i));
-	}
-
-	out = ptr = palloc(buflen);
-
-	src = makeStringInfo();
-	dst = makeStringInfo();
-
-	*ptr++ = '{';
-
-	for (i = 0; i < count; i++)
-	{
-		resetStringInfo(src);
-		resetStringInfo(dst);
-		appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		escape_json(dst, src->data);
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
-		*ptr++ = ':';
-		*ptr++ = ' ';
-		resetStringInfo(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(src);
-			appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
+		str = makeStringInfo();
+		appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
 
-			/*
-			 * don't treat something with a leading zero followed by another
-			 * digit as numeric - could be a zip code or similar
-			 */
-			if (src->len > 0 &&
-				!(src->data[0] == '0' &&
-				  isdigit((unsigned char) src->data[1])) &&
-				strspn(src->data, "+-0123456789Ee.") == src->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(src->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;
+		HStoreToCString(str, VARDATA_ANY(in), VARSIZE_ANY(in), 
+						SET_PRETTY_PRINT_VAR(JsonOutput | RootHashDecorated | LooseOutput));
 
-					dval = strtod(src->data, &endptr);
-					(void) dval;
-					if (*endptr == '\0')
-						is_number = true;
-				}
-			}
-			if (is_number)
-				appendBinaryStringInfo(dst, src->data, src->len);
-			else
-				escape_json(dst, src->data);
-		}
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
+		out = (text*)str->data;
 
-		if (i + 1 != count)
-		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
-		}
+		SET_VARSIZE(out, str->len);
 	}
-	*ptr++ = '}';
-	*ptr = '\0';
 
-	PG_RETURN_TEXT_P(cstring_to_text(out));
+	PG_RETURN_TEXT_P(out);
 }
 
 PG_FUNCTION_INFO_V1(hstore_to_json);
@@ -1350,74 +1596,330 @@ Datum
 hstore_to_json(PG_FUNCTION_ARGS)
 {
 	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);
-	StringInfo	src,
-				dst;
+	text	   *out;
 
-	if (count == 0)
-		PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
+	if (HS_ISEMPTY(in))
+	{
+		out = cstring_to_text_with_len("{}",2);
+	}
+	else
+	{
+		StringInfo	str;
 
-	buflen = 3;
+		str = makeStringInfo();
+		appendBinaryStringInfo(str, "    ", 4); /* VARHDRSZ */
 
-	/*
-	 * Formula adjusted slightly from the logic in hstore_out. We have to take
-	 * account of out treatment of booleans to be a bit more pessimistic about
-	 * the length of values.
-	 */
+		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;
+}
 
-	for (i = 0; i < count; i++)
+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;
+	Oid				castOid = InvalidOid;
+	int				valueType = hsvString;
+	FmgrInfo		castInfo;
+	CoercionMethod	method;
+
+	if (ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)) == 0)
+		PG_RETURN_POINTER(hstoreDump(NULL));
+
+	switch(ARR_ELEMTYPE(array))
 	{
-		/* include "" and colon-space and comma-space */
-		buflen += 6 + 2 * HS_KEYLEN(entries, i);
-		/* include "" only if nonnull */
-		buflen += 3 + (HS_VALISNULL(entries, i)
-					   ? 1
-					   : 2 * HS_VALLEN(entries, i));
+		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");
+
+				valueType = hsvString;
+				break;
+			}
 	}
 
-	out = ptr = palloc(buflen);
+	if (castOid != InvalidOid)
+		fmgr_info(castOid, &castInfo);
 
-	src = makeStringInfo();
-	dst = makeStringInfo();
+	iterator = array_create_iterator(array, 0);
 
-	*ptr++ = '{';
+	value.type = hsvArray;
+	value.array.scalar = false;
+	for(i=0; i<ncounters; i++)
+	{
+		value.array.nelems = dims[i];
+		result = pushHStoreValue(&state, WHS_BEGIN_ARRAY, &value);
+	}
 
-	for (i = 0; i < count; i++)
+	while(array_iterate(iterator, &datum, &isnull))
 	{
-		resetStringInfo(src);
-		resetStringInfo(dst);
-		appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
-		escape_json(dst, src->data);
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
-		*ptr++ = ':';
-		*ptr++ = ' ';
-		resetStringInfo(dst);
-		if (HS_VALISNULL(entries, i))
-			appendStringInfoString(dst, "null");
+		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);
+			}
+		}
 		else
 		{
-			resetStringInfo(src);
-			appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
-			escape_json(dst, src->data);
+			counters[i]++;
 		}
-		strncpy(ptr, dst->data, dst->len);
-		ptr += dst->len;
 
-		if (i + 1 != count)
+		if (isnull)
+		{
+			value.type = hsvNull;
+			value.size = sizeof(HEntry);
+		}
+		else
 		{
-			*ptr++ = ',';
-			*ptr++ = ' ';
+			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, "Impossible state: %d", valueType);
+			}
 		}
+
+		result = pushHStoreValue(&state, WHS_ELEM, &value);
+	}
+
+	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);
+}
+
+PG_FUNCTION_INFO_V1(hstore2jsonb);
+Datum		hstore2jsonb(PG_FUNCTION_ARGS);
+Datum
+hstore2jsonb(PG_FUNCTION_ARGS)
+{
+	HStore	*hs = PG_GETARG_HS(0);
+	Jsonb	*jb = palloc(VARSIZE_ANY(hs));
+
+	memcpy(jb, hs, VARSIZE_ANY(hs));
+
+	if (VARSIZE_ANY_EXHDR(jb) >= sizeof(uint32))
+	{
+		uint32 *header = (uint32*)VARDATA_ANY(jb);
+
+		*header &= ~JB_FLAG_UNUSED;
+	}
+
+	PG_RETURN_JSONB(jb);
+}
+
+PG_FUNCTION_INFO_V1(jsonb2hstore);
+Datum		jsonb2hstore(PG_FUNCTION_ARGS);
+Datum
+jsonb2hstore(PG_FUNCTION_ARGS)
+{
+	Jsonb	*jb = PG_GETARG_JSONB(0);
+	HStore	*hs = palloc(VARSIZE_ANY(jb));
+
+	memcpy(hs, jb, VARSIZE_ANY(jb));
+
+	if (VARSIZE_ANY_EXHDR(hs) >= sizeof(uint32))
+	{
+		uint32	*header = (uint32*)VARDATA_ANY(hs);
+
+		*header |= HS_FLAG_NEWVERSION;
+	}
+
+	PG_RETURN_POINTER(hs);
+}
+
+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);
+
+	if (l > sizeof(uint32))
+	{
+		uint32	*header = (uint32*)buffer;
+
+		*header |= HS_FLAG_NEWVERSION;
 	}
-	*ptr++ = '}';
-	*ptr = '\0';
 
-	PG_RETURN_TEXT_P(cstring_to_text(out));
+	return l;
 }
+
+
+
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 45edb04..0af258c 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -25,154 +25,597 @@ 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;
 
-/*
- * 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)
+	deconstruct_array(a,
+					  TEXTOID, -1, false, 'i',
+					  &key_datums, &key_nulls, &key_count);
+
+	if (key_count == 0)
+		return NULL;
+
+	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)
 {
-	HEntry	   *entries = ARRPTR(hs);
-	int			stopLow = lowbound ? *lowbound : 0;
-	int			stopHigh = HS_COUNT(hs);
-	int			stopMiddle;
-	char	   *base = STRPTR(hs);
+	HStoreValue		*stopLow = a->array.elems + ((lowbound) ? *lowbound : 0),
+					*stopHigh = a->array.elems + a->array.nelems,
+					*stopMiddle;
 
 	while (stopLow < stopHigh)
 	{
-		int			difference;
+		int diff;
 
 		stopMiddle = stopLow + (stopHigh - stopLow) / 2;
 
-		if (HS_KEYLEN(entries, stopMiddle) == keylen)
-			difference = memcmp(HS_KEY(entries, base, stopMiddle), key, keylen);
+		if (keylen == stopMiddle->string.len)
+			diff = memcmp(stopMiddle->string.val, key, keylen);
 		else
-			difference = (HS_KEYLEN(entries, stopMiddle) > keylen) ? 1 : -1;
+			diff = (stopMiddle->string.len > keylen) ? 1 : -1;
 
-		if (difference == 0)
+		if (diff == 0)
 		{
 			if (lowbound)
-				*lowbound = stopMiddle + 1;
+				*lowbound = (stopMiddle - a->array.elems) + 1;
 			return stopMiddle;
 		}
-		else if (difference < 0)
+		else if (diff < 0)
+		{
 			stopLow = stopMiddle + 1;
+		}
 		else
+		{
 			stopHigh = stopMiddle;
+		}
 	}
 
 	if (lowbound)
-		*lowbound = stopLow;
-	return -1;
+		*lowbound = (stopLow - a->array.elems) + 1;
+
+	return NULL;
 }
 
-Pairs *
-hstoreArrayToPairs(ArrayType *a, int *npairs)
+PG_FUNCTION_INFO_V1(hstore_fetchval);
+Datum		hstore_fetchval(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval(PG_FUNCTION_ARGS)
 {
-	Datum	   *key_datums;
-	bool	   *key_nulls;
-	int			key_count;
-	Pairs	   *key_pairs;
-	int			bufsiz;
-	int			i,
-				j;
+	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);
+}
 
-	deconstruct_array(a,
-					  TEXTOID, -1, false, 'i',
-					  &key_datums, &key_nulls, &key_count);
+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));
 
-	if (key_count == 0)
+		memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+		PG_RETURN_NUMERIC(out);
+	}
+
+	PG_RETURN_NULL();
+}
+
+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)
 	{
-		*npairs = 0;
-		return NULL;
+		Numeric		out = palloc(VARSIZE_ANY(v->numeric));
+
+		memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+		PG_RETURN_NUMERIC(out);
 	}
 
-	key_pairs = palloc(sizeof(Pairs) * key_count);
+	PG_RETURN_NULL();
+}
 
-	for (i = 0, j = 0; i < key_count; i++)
+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)
+{
+	bool	negative = false;
+	char 	*p = c;
+
+	*acc = 0;
+
+	while(isspace(*p) && p - c < l)
+		p++;
+
+	if (p - c >= l)
+		return false;
+
+	if (*p == '-')
 	{
-		if (!key_nulls[i])
+		negative = true;
+		p++;
+	}
+	else if (*p == '+')
+	{
+		p++;
+	}
+
+	if (p - c >= l)
+		return false;
+
+
+	while(p - c < l)
+	{
+		if (!isdigit(*p))
+			return false;
+
+		*acc *= 10;
+		*acc += (*p - '0');
+		p++;
+	}
+
+	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)
+		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;
+
+	if (path_len == 0)
+		return v;
+
+	for(i=0; v != NULL && i<path_len; i++)
+	{
+		uint32	header;
+
+		if (v->type != hsvBinary || path_nulls[i])
+			return NULL;
+
+		header = *(uint32*)v->binary.data;
+
+		if (header & HS_FLAG_HASH)
 		{
-			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++;
+			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
+		{
+			elog(PANIC,"wrong header type: %08x", header);
 		}
 	}
 
-	*npairs = hstoreUniquePairs(key_pairs, j, &bufsiz);
+	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;
 
-	return key_pairs;
+	if ((out = HStoreValueToText(hstoreDeepFetch(hs, path))) == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_TEXT_P(out);
 }
 
+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);
 
-PG_FUNCTION_INFO_V1(hstore_fetchval);
-Datum		hstore_fetchval(PG_FUNCTION_ARGS);
+	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);
 Datum
-hstore_fetchval(PG_FUNCTION_ARGS)
+hstore_fetchval_path_boolean(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	HEntry	   *entries = ARRPTR(hs);
-	text	   *out;
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
+	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			*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);
 
-	if (idx < 0 || HS_VALISNULL(entries, idx))
+		Assert(v->type == hsvBinary);
+		SET_VARSIZE(out, VARHDRSZ + v->binary.len);
+		memcpy(VARDATA(out), v->binary.data, v->binary.len);
+	}
+
+	return out;
+}
+
+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)
 		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;
 
-	out = cstring_to_text_with_len(HS_VAL(entries, STRPTR(hs), idx),
-								   HS_VALLEN(entries, idx));
+	if (!HS_ISEMPTY(hs))
+		v = getHStoreValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, i);
 
-	PG_RETURN_TEXT_P(out);
+	if ((out = HStoreValueToHStore(v)) == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_POINTER(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);
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
+	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_RETURN_BOOL(idx >= 0);
+
+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);
+
+	PG_RETURN_BOOL(hstoreDeepFetch(hs, path) != NULL);
 }
 
 
+
 PG_FUNCTION_INFO_V1(hstore_exists_any);
 Datum		hstore_exists_any(PG_FUNCTION_ARGS);
 Datum
 hstore_exists_any(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(keys, &nkeys);
-	int			i;
-	int			lowbound = 0;
-	bool		res = false;
-
+	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;
 	/*
 	 * we exploit the fact that the pairs list is already sorted into strictly
-	 * increasing order to narrow the hstoreFindKey search; each search can
+	 * 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.
 	 */
-	for (i = 0; i < nkeys; i++)
+	for (i = 0; i < v->array.nelems; i++)
 	{
-		int			idx = hstoreFindKey(hs, &lowbound,
-									  key_pairs[i].key, key_pairs[i].keylen);
-
-		if (idx >= 0)
+		if (findUncompressedHStoreValueByValue(VARDATA(hs), HS_FLAG_HASH | HS_FLAG_ARRAY, plowbound,
+											   v->array.elems + i) != NULL)
 		{
 			res = true;
 			break;
@@ -188,26 +631,36 @@ Datum		hstore_exists_all(PG_FUNCTION_ARGS);
 Datum
 hstore_exists_all(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(keys, &nkeys);
-	int			i;
-	int			lowbound = 0;
-	bool		res = true;
+	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);
+	}
 
+	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 hstoreFindKey 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 findUncompressedHStoreValue search;
+	 * each search can start one entry past the previous "found" entry,
+	 * or at the lower bound of the last search.
 	 */
-	for (i = 0; i < nkeys; i++)
+	for (i = 0; i < v->array.nelems; i++)
 	{
-		int			idx = hstoreFindKey(hs, &lowbound,
-									  key_pairs[i].key, key_pairs[i].keylen);
-
-		if (idx < 0)
+		if (findUncompressedHStoreValueByValue(VARDATA(hs),
+											   HS_FLAG_HASH | HS_FLAG_ARRAY,
+											   plowbound,
+											   v->array.elems + i) == NULL)
 		{
 			res = false;
 			break;
@@ -223,14 +676,18 @@ Datum		hstore_defined(PG_FUNCTION_ARGS);
 Datum
 hstore_defined(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	HEntry	   *entries = ARRPTR(hs);
-	int			idx = hstoreFindKey(hs, NULL,
-									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
-	bool		res = (idx >= 0 && !HS_VALISNULL(entries, idx));
-
-	PG_RETURN_BOOL(res);
+	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));
 }
 
 
@@ -239,483 +696,1344 @@ Datum		hstore_delete(PG_FUNCTION_ARGS);
 Datum
 hstore_delete(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	text	   *key = PG_GETARG_TEXT_PP(1);
-	char	   *keyptr = VARDATA_ANY(key);
-	int			keylen = VARSIZE_ANY_EXHDR(key);
-	HStore	   *out = palloc(VARSIZE(hs));
-	char	   *bufs,
-			   *bufd,
-			   *ptrd;
-	HEntry	   *es,
-			   *ed;
-	int			i;
-	int			count = HS_COUNT(hs);
-	int			outcount = 0;
-
-	SET_VARSIZE(out, VARSIZE(hs));
-	HS_SETCOUNT(out, count);	/* temporary! */
+	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);
 
-	bufs = STRPTR(hs);
-	es = ARRPTR(hs);
-	bufd = ptrd = STRPTR(out);
-	ed = ARRPTR(out);
+	it = HStoreIteratorInit(VARDATA(in));
 
-	for (i = 0; i < count; ++i)
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		int			len = HS_KEYLEN(es, i);
-		char	   *ptrs = HS_KEY(es, bufs, i);
+		skipNested = true;
 
-		if (!(len == keylen && memcmp(ptrs, keyptr, keylen) == 0))
+		if ((r == WHS_ELEM || r == WHS_KEY) &&
+			(v.type == hsvString && keylen == v.string.len &&
+			 memcmp(keyptr, v.string.val, keylen) == 0))
 		{
-			int			vallen = HS_VALLEN(es, i);
+			if (r == WHS_KEY)
+				/* skip corresponding value */
+				HStoreIteratorGet(&it, &v, true);
 
-			HS_COPYITEM(ed, bufd, ptrd, ptrs, len, vallen, HS_VALISNULL(es, i));
-			++outcount;
+			continue;
 		}
+
+		res = pushHStoreValue(&toState, r, &v);
 	}
 
-	HS_FINALIZE(out, outcount, bufd, ptrd);
+	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);
+	}
 
 	PG_RETURN_POINTER(out);
 }
 
-
 PG_FUNCTION_INFO_V1(hstore_delete_array);
 Datum		hstore_delete_array(PG_FUNCTION_ARGS);
 Datum
 hstore_delete_array(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HStore	   *out = palloc(VARSIZE(hs));
-	int			hs_count = HS_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)
+	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)
 	{
-		/* return a copy of the input, unchanged */
-		memcpy(out, hs, VARSIZE(hs));
-		HS_FIXSIZE(out, hs_count);
-		HS_SETCOUNT(out, hs_count);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	/*
-	 * this is in effect a merge between hs and key_pairs, both of which are
-	 * already sorted by (keylen,key); we take keys from hs only
-	 */
+	it = HStoreIteratorInit(VARDATA(in));
 
-	for (i = j = 0; i < hs_count;)
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		int			difference;
 
-		if (j >= nkeys)
-			difference = -1;
-		else
+		if (skipNested == false)
 		{
-			int			skeylen = HS_KEYLEN(es, i);
-
-			if (skeylen == key_pairs[j].keylen)
-				difference = memcmp(HS_KEY(es, ps, i),
-									key_pairs[j].key,
-									key_pairs[j].keylen);
-			else
-				difference = (skeylen > key_pairs[j].keylen) ? 1 : -1;
+			Assert(v.type == hsvArray || v.type == hsvHash);
+			isHash = (v.type == hsvArray) ? false : true;
+			skipNested = true;
 		}
 
-		if (difference > 0)
-			++j;
-		else if (difference == 0)
-			++i, ++j;
-		else
+		if ((r == WHS_ELEM || r == WHS_KEY) && v.type == hsvString &&
+			i < a->array.nelems)
 		{
-			HS_COPYITEM(ed, bufd, pd,
-						HS_KEY(es, ps, i), HS_KEYLEN(es, i),
-						HS_VALLEN(es, i), HS_VALISNULL(es, i));
-			++outcount;
-			++i;
-		}
-	}
+			int diff;
 
-	HS_FINALIZE(out, outcount, bufd, pd);
+			if (isHash)
+			{
+				do {
+					diff = compareHStoreStringValue(&v, a->array.elems + i,
+													NULL);
 
-	PG_RETURN_POINTER(out);
-}
+					if (diff >= 0)
+						i++;
+				} while(diff > 0 && i < a->array.nelems);
+			}
+			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);
 
-PG_FUNCTION_INFO_V1(hstore_delete_hstore);
-Datum		hstore_delete_hstore(PG_FUNCTION_ARGS);
-Datum
-hstore_delete_hstore(PG_FUNCTION_ARGS)
+				continue;
+			}
+		}
+
+		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);
+	}
+
+	PG_RETURN_POINTER(out);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_delete_hstore);
+Datum		hstore_delete_hstore(PG_FUNCTION_ARGS);
+Datum
+hstore_delete_hstore(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HStore	   *hs2 = PG_GETARG_HS(1);
-	HStore	   *out = palloc(VARSIZE(hs));
-	int			hs_count = HS_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);
+	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));
 		PG_RETURN_POINTER(out);
 	}
 
-	/*
-	 * this is in effect a merge between hs and hs2, both of which are already
-	 * sorted by (keylen,key); we take keys from hs only; for equal keys, we
-	 * take the value from hs unless the values are equal
-	 */
+	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);
 
-	for (i = j = 0; i < hs_count;)
+	if (isHash1 == true && isHash2 == true)
 	{
-		int			difference;
+		bool			fin2 = false,
+						keyIsDef = false;
 
-		if (j >= hs2_count)
-			difference = -1;
-		else
+		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)
 		{
-			int			skeylen = HS_KEYLEN(es, i);
-			int			s2keylen = HS_KEYLEN(es2, j);
 
-			if (skeylen == s2keylen)
-				difference = memcmp(HS_KEY(es, ps, i),
-									HS_KEY(es2, ps2, j),
-									skeylen);
+			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);
+
+	if (r == WHS_BEGIN_ARRAY)
+	{
+		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
-				difference = (skeylen > s2keylen) ? 1 : -1;
+				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;
+		}
+
+		if (level == path_len - 1)
+		{
+			/* last level in path, skip all elem */
+			r = HStoreIteratorGet(it, &v, true);
+			Assert(r == WHS_ELEM);
+		}
+		else
+		{
+			res = deletePathDo(it, path_elems, path_nulls, path_len, st,
+							   level + 1);
+		}
+
+		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);
+		}
+
+		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);
+
+			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)
+			{
+				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;
+			}
+
+			pushHStoreValue(st, r, &k);
+			r = HStoreIteratorGet(it, &v, true);
+			Assert(r == WHS_VALUE);
+			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) /* just a string or null */
+	{
+		pushHStoreValue(st, r, &v);
+		res = (void*)0x01; /* dummy value */
+	}
+	else
+	{
+		elog(PANIC, "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;
+			}
+		}
+
+		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);
+	}
+
+	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(PANIC,"Could not convert to string");
+	}
+}
+
+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");
 
-		if (difference > 0)
-			++j;
-		else if (difference == 0)
+		res = pushHStoreValue(toState, r1, &v1);
+
+		for(;;)
 		{
-			int			svallen = HS_VALLEN(es, i);
-			int			snullval = HS_VALISNULL(es, i);
+			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);
+		}
 
-			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)))
+		while((r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
+		{
+			if (!(r2 == WHS_END_HASH || r2 == WHS_END_ARRAY))
 			{
-				HS_COPYITEM(ed, bufd, pd,
-							HS_KEY(es, ps, i), HS_KEYLEN(es, i),
-							svallen, snullval);
-				++outcount;
+				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);
+				}
 			}
-			++i, ++j;
+		}
+
+		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
 		{
-			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, 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(PANIC, "impossible state");
 	}
 
-	HS_FINALIZE(out, outcount, bufd, pd);
-
-	PG_RETURN_POINTER(out);
+	return res;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_concat);
-Datum		hstore_concat(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_replace);
+Datum		hstore_replace(PG_FUNCTION_ARGS);
 Datum
-hstore_concat(PG_FUNCTION_ARGS)
+hstore_replace(PG_FUNCTION_ARGS)
 {
-	HStore	   *s1 = PG_GETARG_HS(0);
-	HStore	   *s2 = PG_GETARG_HS(1);
-	HStore	   *out = palloc(VARSIZE(s1) + VARSIZE(s2));
-	char	   *ps1,
-			   *ps2,
-			   *bufd,
-			   *pd;
-	HEntry	   *es1,
-			   *es2,
-			   *ed;
-	int			s1idx;
-	int			s2idx;
-	int			s1count = 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);
+	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));
 		PG_RETURN_POINTER(out);
 	}
 
-	if (s2count == 0)
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &path_elems, &path_nulls, &path_len);
+
+	if (path_len == 0)
 	{
-		/* return a copy of the input, unchanged */
-		memcpy(out, s1, VARSIZE(s1));
-		HS_FIXSIZE(out, s1count);
-		HS_SETCOUNT(out, s1count);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	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)
+	if (HS_ROOT_COUNT(newval) == 0)
 	{
-		int			difference;
+		value.type = hsvNull;
+		value.size = sizeof(HEntry);
+	}
+	else
+	{
+		value.type = hsvBinary;
+		value.binary.data = VARDATA(newval);
+		value.binary.len = VARSIZE_ANY_EXHDR(newval);
+		value.size = value.binary.len + sizeof(HEntry);
+	}
 
-		if (s1idx >= s1count)
-			difference = 1;
-		else if (s2idx >= s2count)
-			difference = -1;
-		else
-		{
-			int			s1keylen = HS_KEYLEN(es1, s1idx);
-			int			s2keylen = HS_KEYLEN(es2, s2idx);
+	it = HStoreIteratorInit(VARDATA(in));
 
-			if (s1keylen == s2keylen)
-				difference = memcmp(HS_KEY(es1, ps1, s1idx),
-									HS_KEY(es2, ps2, s2idx),
-									s1keylen);
-			else
-				difference = (s1keylen > s2keylen) ? 1 : -1;
-		}
+	res = replacePathDo(&it, path_elems, path_nulls, path_len, &st, 0, &value);
 
-		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;
-		}
+	if (res == NULL)
+	{
+		SET_VARSIZE(out, VARHDRSZ);
 	}
+	else
+	{
+		int				sz;
 
-	HS_FINALIZE(out, outcount, bufd, pd);
+		sz = compressHStore(res, VARDATA(out));
+		SET_VARSIZE(out, sz + 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)
+static HStoreValue*
+concatPathDo(HStoreIterator **it, Datum *path_elems,
+			 bool *path_nulls, int path_len,
+			 ToHStoreState  **st, int level, HStoreIterator	*toConcat)
 {
-	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;
+	HStoreValue v, *res = NULL;
+	int			r;
 
-	deconstruct_array(key_array,
-					  TEXTOID, -1, false, 'i',
-					  &key_datums, &key_nulls, &key_count);
+	r = HStoreIteratorGet(it, &v, false);
 
-	if (key_count == 0)
+	if (r == WHS_BEGIN_ARRAY)
 	{
-		aout = construct_empty_array(TEXTOID);
-		PG_RETURN_POINTER(aout);
-	}
+		int		idx, i;
+		uint32	n = v.array.nelems;
 
-	out_datums = palloc(sizeof(Datum) * key_count);
-	out_nulls = palloc(sizeof(bool) * key_count);
+		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;
+		}
 
-	for (i = 0; i < key_count; ++i)
-	{
-		text	   *key = (text *) DatumGetPointer(key_datums[i]);
-		int			idx;
+		if (idx > n)
+			idx = n;
 
-		if (key_nulls[i])
-			idx = -1;
-		else
-			idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ);
+		pushHStoreValue(st, r, &v);
 
-		if (idx < 0 || HS_VALISNULL(entries, idx))
+		for(i=0; i<n; i++)
 		{
-			out_nulls[i] = true;
-			out_datums[i] = (Datum) 0;
+			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);
+			}
 		}
-		else
+
+		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++)
 		{
-			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, &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);
+			}
 		}
-	}
 
-	aout = construct_md_array(out_datums, out_nulls,
-							  ARR_NDIM(key_array),
-							  ARR_DIMS(key_array),
-							  ARR_LBOUND(key_array),
-							  TEXTOID, -1, false, 'i');
+		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(PANIC, "impossible state");
+	}
 
-	PG_RETURN_POINTER(aout);
+	return res;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_slice_to_hstore);
-Datum		hstore_slice_to_hstore(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_deep_concat);
+Datum		hstore_deep_concat(PG_FUNCTION_ARGS);
 Datum
-hstore_slice_to_hstore(PG_FUNCTION_ARGS)
+hstore_deep_concat(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *ptr = STRPTR(hs);
-	ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(1);
-	HStore	   *out;
-	int			nkeys;
-	Pairs	   *key_pairs = hstoreArrayToPairs(key_array, &nkeys);
-	Pairs	   *out_pairs;
-	int			bufsiz;
-	int			lastidx = 0;
-	int			i;
-	int			out_count = 0;
-
-	if (nkeys == 0)
+	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")));
+
+	if (HS_ROOT_COUNT(in) == 0 || HS_ROOT_COUNT(newval) == 0)
 	{
-		out = hstorePairs(NULL, 0, 0);
+		memcpy(out, in, VARSIZE(in));
 		PG_RETURN_POINTER(out);
 	}
 
-	out_pairs = palloc(sizeof(Pairs) * nkeys);
-	bufsiz = 0;
+	deconstruct_array(path, TEXTOID, -1, false, 'i',
+					  &path_elems, &path_nulls, &path_len);
 
-	/*
-	 * 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.
-	 */
+	it1 = HStoreIteratorInit(VARDATA(in));
+	it2 = HStoreIteratorInit(VARDATA(newval));
 
-	for (i = 0; i < nkeys; ++i)
-	{
-		int			idx = hstoreFindKey(hs, &lastidx,
-									  key_pairs[i].key, key_pairs[i].keylen);
+	if (path_len == 0)
+		res = IteratorConcat(&it1, &it2, &st);
+	else
+		res = concatPathDo(&it1, path_elems, path_nulls, path_len, &st, 0, it2);
 
-		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;
-		}
+	if (res == NULL)
+	{
+		SET_VARSIZE(out, VARHDRSZ);
 	}
+	else
+	{
+		int				sz;
 
-	/*
-	 * we don't use uniquePairs here because we know that the pairs list is
-	 * already sorted and uniq'ed.
-	 */
+		if (res->type == hsvArray && res->array.nelems > 1)
+			res->array.scalar = false;
 
-	out = hstorePairs(out_pairs, out_count, bufsiz);
+		sz = compressHStore(res, VARDATA(out));
+		SET_VARSIZE(out, sz + VARHDRSZ);
+	}
 
 	PG_RETURN_POINTER(out);
 }
 
-
 PG_FUNCTION_INFO_V1(hstore_akeys);
 Datum		hstore_akeys(PG_FUNCTION_ARGS);
 Datum
 hstore_akeys(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	Datum	   *d;
-	ArrayType  *a;
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *base = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			i;
-
-	if (count == 0)
+	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))
 	{
 		a = construct_empty_array(TEXTOID);
 		PG_RETURN_POINTER(a);
 	}
 
-	d = (Datum *) palloc(sizeof(Datum) * count);
+	d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
 
-	for (i = 0; i < count; ++i)
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		text	   *item = cstring_to_text_with_len(HS_KEY(entries, base, i),
-													HS_KEYLEN(entries, i));
+		skipNested = true;
 
-		d[i] = PointerGetDatum(item);
+		if ((r == WHS_ELEM && v.type != hsvNull) || r == WHS_KEY)
+			d[i++] = PointerGetDatum(HStoreValueToText(&v));
 	}
 
-	a = construct_array(d, count,
+	a = construct_array(d, i,
 						TEXTOID, -1, false, 'i');
 
 	PG_RETURN_POINTER(a);
@@ -727,43 +2045,40 @@ Datum		hstore_avals(PG_FUNCTION_ARGS);
 Datum
 hstore_avals(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs = PG_GETARG_HS(0);
-	Datum	   *d;
-	bool	   *nulls;
-	ArrayType  *a;
-	HEntry	   *entries = ARRPTR(hs);
-	char	   *base = STRPTR(hs);
-	int			count = HS_COUNT(hs);
-	int			lb = 1;
-	int			i;
-
-	if (count == 0)
+	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))
 	{
 		a = construct_empty_array(TEXTOID);
 		PG_RETURN_POINTER(a);
 	}
 
-	d = (Datum *) palloc(sizeof(Datum) * count);
-	nulls = (bool *) palloc(sizeof(bool) * count);
+	d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
+	nulls = (bool *) palloc(sizeof(bool) * HS_ROOT_COUNT(hs));
 
-	for (i = 0; i < count; ++i)
+	it = HStoreIteratorInit(VARDATA(hs));
+
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
 	{
-		if (HS_VALISNULL(entries, i))
-		{
-			d[i] = (Datum) 0;
-			nulls[i] = true;
-		}
-		else
-		{
-			text	   *item = cstring_to_text_with_len(HS_VAL(entries, base, i),
-													  HS_VALLEN(entries, i));
+		skipNested = true;
 
-			d[i] = PointerGetDatum(item);
-			nulls[i] = false;
+		if (r == WHS_ELEM || r == WHS_VALUE)
+		{
+			d[i] = PointerGetDatum(HStoreValueToText(&v));
+			nulls[i] = (DatumGetPointer(d[i]) == NULL) ? true : false;
+			i++;
 		}
 	}
 
-	a = construct_md_array(d, nulls, 1, &count, &lb,
+	a = construct_md_array(d, nulls, 1, &i, &lb,
 						   TEXTOID, -1, false, 'i');
 
 	PG_RETURN_POINTER(a);
@@ -773,44 +2088,53 @@ hstore_avals(PG_FUNCTION_ARGS)
 static ArrayType *
 hstore_to_array_internal(HStore *hs, int ndims)
 {
-	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;
+	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;
 
 	Assert(ndims < 3);
 
 	if (count == 0 || ndims == 0)
 		return construct_empty_array(TEXTOID);
 
-	out_size[0] = count * 2 / ndims;
+	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_datums = palloc(sizeof(Datum) * count * 2);
 	out_nulls = palloc(sizeof(bool) * count * 2);
 
-	for (i = 0; i < count; ++i)
-	{
-		text	   *key = cstring_to_text_with_len(HS_KEY(entries, base, i),
-												   HS_KEYLEN(entries, i));
+	it = HStoreIteratorInit(VARDATA(hs));
 
-		out_datums[i * 2] = PointerGetDatum(key);
-		out_nulls[i * 2] = false;
+	while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
 
-		if (HS_VALISNULL(entries, i))
-		{
-			out_datums[i * 2 + 1] = (Datum) 0;
-			out_nulls[i * 2 + 1] = true;
-		}
-		else
+		switch(r)
 		{
-			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;
+			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;
 		}
 	}
 
@@ -850,222 +2174,526 @@ hstore_to_matrix(PG_FUNCTION_ARGS)
  * there was no explanatory comment in the original code. --AG)
  */
 
-static void
-setup_firstcall(FuncCallContext *funcctx, HStore *hs,
-				FunctionCallInfoData *fcinfo)
-{
-	MemoryContext oldcontext;
-	HStore	   *st;
+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;
+	}
+
+	while(st->level >= 0)
+	{
+		uint32	header;
 
-	oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+		v = NULL;
+		if (st->path[st->level].v.type != hsvBinary)
+		{
+			st->level--;
+			continue;
+		}
 
-	st = (HStore *) palloc(VARSIZE(hs));
-	memcpy(st, hs, VARSIZE(hs));
+		header = *(uint32*)st->path[st->level].v.binary.data;
 
-	funcctx->user_fctx = (void *) st;
+		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;
 
-	if (fcinfo)
-	{
-		TupleDesc	tupdesc;
+				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;
+					}
+				}
 
-		/* 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");
+				v = getHStoreValue(st->path[st->level].v.binary.data,
+								   HS_FLAG_ARRAY, ith);
+			}
+			else
+			{
+				st->level--;
+				continue;
+			}
+		}
+		else
+		{
+			elog(PANIC, "impossible state");
+		}
 
-		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+		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;
+		}
 	}
 
-	MemoryContextSwitchTo(oldcontext);
+	return v;
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_skeys);
-Datum		hstore_skeys(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_svals_path);
+Datum		hstore_svals_path(PG_FUNCTION_ARGS);
 Datum
-hstore_skeys(PG_FUNCTION_ARGS)
+hstore_svals_path(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	HStoreValue			*v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, NULL);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	if ((v = getNextValsPath(st)) != NULL)
 	{
-		HEntry	   *entries = ARRPTR(hs);
-		text	   *item;
+		text	*item = HStoreValueToText(v);
 
-		item = cstring_to_text_with_len(HS_KEY(entries, STRPTR(hs), i),
-										HS_KEYLEN(entries, i));
-
-		SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
+		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);
+PG_FUNCTION_INFO_V1(hstore_hvals_path);
+Datum		hstore_hvals_path(PG_FUNCTION_ARGS);
 Datum
-hstore_svals(PG_FUNCTION_ARGS)
+hstore_hvals_path(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	HStoreValue 		*v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, NULL);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0),
+							 PG_GETARG_ARRAYTYPE_P(1), NULL);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	if ((v = getNextValsPath(st)) != NULL)
 	{
-		HEntry	   *entries = ARRPTR(hs);
+		HStore	   *item = HStoreValueToHStore(v);
 
-		if (HS_VALISNULL(entries, i))
-		{
-			ReturnSetInfo *rsi;
-
-			/* ugly ugly ugly. why no macro for this? */
-			(funcctx)->call_cntr++;
-			rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-			rsi->isDone = ExprMultipleResult;
-			PG_RETURN_NULL();
-		}
+		if (item == NULL)
+			SRF_RETURN_NEXT_NULL(funcctx);
 		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_contains);
-Datum		hstore_contains(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_each);
+Datum		hstore_each(PG_FUNCTION_ARGS);
 Datum
-hstore_contains(PG_FUNCTION_ARGS)
+hstore_each(PG_FUNCTION_ARGS)
 {
-	HStore	   *val = PG_GETARG_HS(0);
-	HStore	   *tmpl = PG_GETARG_HS(1);
-	bool		res = true;
-	HEntry	   *te = ARRPTR(tmpl);
-	char	   *tstr = STRPTR(tmpl);
-	HEntry	   *ve = ARRPTR(val);
-	char	   *vstr = STRPTR(val);
-	int			tcount = HS_COUNT(tmpl);
-	int			lastidx = 0;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	int					r;
+	HStoreValue			v;
 
-	/*
-	 * 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
-	 */
+	if (SRF_IS_FIRSTCALL())
+	{
+		funcctx = SRF_FIRSTCALL_INIT();
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	for (i = 0; res && i < tcount; ++i)
+	while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
 	{
-		int			idx = hstoreFindKey(val, &lastidx,
-									  HS_KEY(te, tstr, i), HS_KEYLEN(te, i));
+		Datum		res,
+					dvalues[2] = {0, 0};
+		bool		nulls[2] = {false, false};
+		text	   *item;
+		HeapTuple	tuple;
 
-		if (idx >= 0)
+		if (r == WHS_ELEM)
 		{
-			bool		nullval = HS_VALISNULL(te, i);
-			int			vallen = HS_VALLEN(te, i);
+			nulls[0] = true;
 
-			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;
+			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);
 		}
 		else
-			res = false;
-	}
+		{
+			continue;
+		}
 
-	PG_RETURN_BOOL(res);
-}
+		tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
+		res = HeapTupleGetDatum(tuple);
 
+		SRF_RETURN_NEXT(funcctx, PointerGetDatum(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)
-										));
+	SRF_RETURN_DONE(funcctx);
 }
 
-
-PG_FUNCTION_INFO_V1(hstore_each);
-Datum		hstore_each(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_each_hstore);
+Datum		hstore_each_hstore(PG_FUNCTION_ARGS);
 Datum
-hstore_each(PG_FUNCTION_ARGS)
+hstore_each_hstore(PG_FUNCTION_ARGS)
 {
-	FuncCallContext *funcctx;
-	HStore	   *hs;
-	int			i;
+	FuncCallContext 	*funcctx;
+	SetReturningState	*st;
+	int					r;
+	HStoreValue			v;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		hs = PG_GETARG_HS(0);
 		funcctx = SRF_FIRSTCALL_INIT();
-		setup_firstcall(funcctx, hs, fcinfo);
+		st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
 	}
 
 	funcctx = SRF_PERCALL_SETUP();
-	hs = (HStore *) funcctx->user_fctx;
-	i = funcctx->call_cntr;
+	st = (SetReturningState *) funcctx->user_fctx;
 
-	if (i < HS_COUNT(hs))
+	while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
 	{
-		HEntry	   *entries = ARRPTR(hs);
-		char	   *ptr = STRPTR(hs);
 		Datum		res,
-					dvalues[2];
+					dvalues[2] = {0, 0};
 		bool		nulls[2] = {false, false};
 		text	   *item;
+		HStore		*hitem;
 		HeapTuple	tuple;
 
-		item = cstring_to_text_with_len(HS_KEY(entries, ptr, i),
-										HS_KEYLEN(entries, i));
-		dvalues[0] = PointerGetDatum(item);
+		if (r == WHS_ELEM)
+		{
+			nulls[0] = true;
 
-		if (HS_VALISNULL(entries, i))
+			hitem = HStoreValueToHStore(&v);
+			if (hitem == NULL)
+				nulls[1] = true;
+			else
+				dvalues[1] = PointerGetDatum(hitem);
+		}
+		else if (r == WHS_KEY)
 		{
-			dvalues[1] = (Datum) 0;
-			nulls[1] = true;
+			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);
 		}
 		else
 		{
-			item = cstring_to_text_with_len(HS_VAL(entries, ptr, i),
-											HS_VALLEN(entries, i));
-			dvalues[1] = PointerGetDatum(item);
+			continue;
 		}
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
@@ -1077,6 +2705,183 @@ hstore_each(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(PANIC, "impossible state");
+	}
+
+	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
@@ -1089,72 +2894,28 @@ Datum		hstore_cmp(PG_FUNCTION_ARGS);
 Datum
 hstore_cmp(PG_FUNCTION_ARGS)
 {
-	HStore	   *hs1 = PG_GETARG_HS(0);
-	HStore	   *hs2 = PG_GETARG_HS(1);
-	int			hcount1 = HS_COUNT(hs1);
-	int			hcount2 = HS_COUNT(hs2);
-	int			res = 0;
+	HStore	   		*hs1 = PG_GETARG_HS(0);
+	HStore	   		*hs2 = PG_GETARG_HS(1);
+	int				res;
 
-	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) || HS_ISEMPTY(hs2))
 	{
-		/* 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(hs1))
 		{
-			if (len1 > len2)
-				res = 1;
-			else if (len1 < len2)
-				res = -1;
-			else if (hcount1 > hcount2)
-				res = 1;
-			else if (hcount2 > hcount1)
-				res = -1;
+			if (HS_ISEMPTY(hs2))
+				res = 0;
 			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;
-				}
-			}
+				res = -1;
 		}
 		else
 		{
-			res = (res > 0) ? 1 : -1;
+			res = 1;
 		}
 	}
+	else
+	{
+		res = compareHStoreBinaryValue(VARDATA(hs1), VARDATA(hs2));
+	}
 
 	/*
 	 * this is a btree support function; this is one of the few places where
@@ -1248,17 +3009,61 @@ 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
new file mode 100644
index 0000000..ab9f67e
--- /dev/null
+++ b/contrib/hstore/hstore_scan.l
@@ -0,0 +1,311 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
+
diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql
index 9518f56..abf3c0e 100644
--- a/contrib/hstore/sql/hstore.sql
+++ b/contrib/hstore/sql/hstore.sql
@@ -39,6 +39,9 @@ select 'aa=>"bb" ,cc=>dd'::hstore;
 select 'aa=>null'::hstore;
 select 'aa=>NuLl'::hstore;
 select 'aa=>"NuLl"'::hstore;
+select 'aa=>nul'::hstore;
+select 'aa=>NuL'::hstore;
+select 'aa=>"NuL"'::hstore;
 
 select e'\\=a=>q=w'::hstore;
 select e'"=a"=>q\\=w'::hstore;
@@ -213,7 +216,7 @@ select hstore(v) from testhstore1 v;
 select hstore(null::testhstore0);
 select hstore(null::testhstore1);
 select pg_column_size(hstore(v))
-         = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore)
+         = pg_column_size('a=>1, b=>"foo", c=>1.2, d=>3, e=>0'::hstore)
   from testhstore1 v;
 select populate_record(v, hstore('c', '3.45')) from testhstore1 v;
 select populate_record(v, hstore('d', '3.45')) from testhstore1 v;
@@ -299,6 +302,8 @@ select count(*) from testhstore where h ? 'public';
 select count(*) from testhstore where h ?| ARRAY['public','disabled'];
 select count(*) from testhstore where h ?& ARRAY['public','disabled'];
 
+RESET enable_seqscan;
+
 drop index hidx;
 create index hidx on testhstore using gin (h);
 set enable_seqscan=off;
@@ -310,6 +315,8 @@ select count(*) from testhstore where h ? 'public';
 select count(*) from testhstore where h ?| ARRAY['public','disabled'];
 select count(*) from testhstore where h ?& ARRAY['public','disabled'];
 
+RESET enable_seqscan;
+
 select count(*) from (select (each(h)).key from testhstore) as wow ;
 select key, count(*) from (select (each(h)).key from testhstore) as wow group by key order by count desc, key;
 
@@ -323,6 +330,9 @@ select count(*) from (select h from (select * from testhstore union all select *
 select distinct * from (values (hstore '' || ''),('')) v(h);
 set enable_sort = true;
 
+RESET enable_hashagg;
+RESET enable_sort;
+
 -- btree
 drop index hidx;
 create index hidx on testhstore using btree (h);
@@ -331,6 +341,18 @@ set enable_seqscan=off;
 select count(*) from testhstore where h #># 'p=>1';
 select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
 
+--gin hash
+drop index hidx;
+create index hidx on testhstore using gin (h gin_hstore_hash_ops);
+set enable_seqscan=off;
+
+select count(*) from testhstore where h @> 'wait=>NULL';
+select count(*) from testhstore where h @> 'wait=>CC';
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+
+RESET enable_seqscan;
+drop index hidx;
+
 -- json
 select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
 select cast( hstore  '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
diff --git a/contrib/hstore/sql/nested.sql b/contrib/hstore/sql/nested.sql
new file mode 100644
index 0000000..a998650
--- /dev/null
+++ b/contrib/hstore/sql/nested.sql
@@ -0,0 +1,480 @@
+
+SELECT 'ff => {a=>12, b=>16}'::hstore;
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123'::hstore;
+
+SELECT 'aa => {a,aaa}, qq=>{ a=>12, b=>16 , c=> { c1, c2}, d=>{d1=>d1, d2=>d2, d1=>d3} }'::hstore;
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore;
+
+SELECT 'ff => {a,aaa}'::hstore;
+
+
+select 'null'::hstore;
+select '{null}'::hstore;
+select ''::hstore;
+select '{}'::hstore;
+
+--test optional outer braces
+SELECT	'a=>1'::hstore;
+SELECT	'{a=>1}'::hstore;
+SELECT	'{a,b}'::hstore;
+SELECT	'{a,{b}}'::hstore;
+SELECT	'{{a},b}'::hstore;
+SELECT	'{a,{b},{c}}'::hstore;
+SELECT	'{{a},{b},c}'::hstore;
+SELECT	'{{a},b,{c}}'::hstore;
+SELECT	'{a,{b=>1}}'::hstore;
+SELECT	'{{a},{b=>1}}'::hstore;
+SELECT	'a'::hstore;
+SELECT	'{a}'::hstore;
+SELECT	''::hstore;
+SELECT	'{}'::hstore;
+
+--nested json
+
+SELECT	hstore_to_json('a=>1');
+SELECT	hstore_to_json('{a=>1}');
+SELECT	hstore_to_json('{a,b}');
+SELECT	hstore_to_json('{a,{b}}');
+SELECT	hstore_to_json('{{a},b}');
+SELECT	hstore_to_json('{a,{b},{c}}');
+SELECT	hstore_to_json('{{a},{b},c}');
+SELECT	hstore_to_json('{{a},b,{c}}');
+SELECT	hstore_to_json('{a,{b=>1}}');
+SELECT	hstore_to_json('{{a},{b=>1}}');
+SELECT	hstore_to_json('{{a},{b=>1},{c}}');
+SELECT	hstore_to_json('a');
+SELECT	hstore_to_json('{a}');
+SELECT	hstore_to_json('');
+SELECT	hstore_to_json('{}');
+
+SELECT hstore_to_json('"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore);
+
+--
+
+SELECT 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'ff', 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'qq', 
+	   ('ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'Y') IS NULL AS t, 
+	   'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'x'; 
+
+SELECT '[ a, b, c, d]'::hstore -> 'a';
+--
+
+CREATE TABLE testtype (i int, h hstore, a int[], j json);
+INSERT INTO testtype VALUES (1, 'a=>1', '{1,2,3}', '{"x": 2}');
+
+SELECT populate_record(v, 'i=>2'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, a=>{7,8,9}'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}'::hstore) FROM testtype v;
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}'::hstore) FROM testtype v;
+
+--complex delete
+
+SELECT 'b=>{a,c}'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>1'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text;
+SELECT '[2,3,a]'::hstore - 'a'::text;
+SELECT '[a,2,3,a]'::hstore - 'a'::text;
+SELECT '[a,a]'::hstore - 'a'::text;
+SELECT '[a]'::hstore - 'a'::text;
+SELECT 'a=>1'::hstore - 'a'::text;
+SELECT ''::hstore - 'a'::text;
+
+SELECT '{a, 1 , b,2, c,3}'::hstore - ARRAY['d','b'];
+
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>23'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>{1,2}'::hstore;
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'a=>{1,2}'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v=>23'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,23]'::hstore;
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,{1,2}]'::hstore;
+
+--joining
+
+SELECT 'aa=>1 , b=>2, cq=>3'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+SELECT '{aa,1 , b,2, cq,3}'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore;
+SELECT  'x'::hstore || 'a=>"1"':: hstore;
+
+--slice
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['b','c']);
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+SELECT slice_array(hstore 'aa=>1, b=>{2=>1}, c=>{1,2}', ARRAY['b','c']);
+
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['g','h','i']);
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']);
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['b','c']);
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']);
+SELECT slice(hstore '{aa=>1, b=>{2=>1}, c=>{1,2}}', ARRAY['b','c']);
+
+--to array
+SELECT %% 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL';
+SELECT %% '{aa,1, cq,l, b,g, fg,NULL}';
+SELECT hstore_to_matrix( 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL');
+SELECT hstore_to_matrix( '{aa,1, cq,l, b,g, fg,NULL}');
+
+
+--contains
+SELECT 'a=>b'::hstore @> 'a=>b, c=>b';
+SELECT 'a=>b, c=>b'::hstore @> 'a=>b';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{2,1}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1,2}';
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1=>2}';
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1=>2}';
+SELECT '{a,b}'::hstore @> '{a,b, c,b}';
+SELECT '{a,b, c,b}'::hstore @> '{a,b}';
+SELECT '{a,b, c,{1,2}}'::hstore @> '{a,{1,2}}';
+SELECT '{a,b, c,{1,2}}'::hstore @> '{b,{1,2}}';
+
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1}';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{2}';
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{3}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{c=>3}}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4}}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},3}';
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},1}';
+
+-- %>
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'n';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'a';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'b';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'c';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd' %> '1';
+
+SELECT '[1,2,3,{a,b}]'::hstore %> '1';
+SELECT '["1",2,3,{a,b}]'::hstore %> '1';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 0;
+
+-- ->
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 0;
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -6;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -1;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -6;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -1;
+
+-- #>
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{a}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -4}';
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{0}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{3}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4,5}';
+
+-- #%>
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{a}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 0}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -1}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -2}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -3}';
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -4}';
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{0}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{3}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4}';
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4,5}';
+
+-- ?
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 1;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 0;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 1;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 0;
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -6;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -5;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -4;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -3;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -2;
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -1;
+
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -6;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -5;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -4;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -3;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -2;
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -1;
+
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{0}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{a}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{b}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 0}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 1}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 2}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 3}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -1}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -2}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -3}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -4}'::text[];
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -5}'::text[];
+
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{0}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{3}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4}'::text[];
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4,5}'::text[];
+
+--deep delete
+
+SELECT 'a=>1'::hstore #- '{x}';
+SELECT 'a=>1'::hstore #- '{a}';
+SELECT 'a=>1'::hstore #- '{NULL}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c}';
+SELECT 'a=>1'::hstore #- '{x,1}';
+SELECT 'a=>1'::hstore #- '{a,1}';
+SELECT 'a=>1'::hstore #- '{NULL,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b,1}';
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c,1}';
+
+SELECT '[a]'::hstore #- '{2}';
+SELECT '[a]'::hstore #- '{1}';
+SELECT '[a]'::hstore #- '{0}';
+SELECT '[a]'::hstore #- '{-1}';
+SELECT '[a]'::hstore #- '{-2}';
+
+SELECT '[a,b,c]'::hstore #- '{3}';
+SELECT '[a,b,c]'::hstore #- '{2}';
+SELECT '[a,b,c]'::hstore #- '{1}';
+SELECT '[a,b,c]'::hstore #- '{0}';
+SELECT '[a,b,c]'::hstore #- '{-1}';
+SELECT '[a,b,c]'::hstore #- '{-2}';
+SELECT '[a,b,c]'::hstore #- '{-3}';
+SELECT '[a,b,c]'::hstore #- '{-4}';
+
+SELECT '[a,b,c]'::hstore #- '{0,0}';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{x}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{a}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d}';
+
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}' #- '{b, -1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 2}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, -2}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 1}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}';
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}' #- '{d, 1, 0}';
+
+-- delete(int)
+
+SELECT '[a,b,c]'::hstore - 3;
+SELECT '[a,b,c]'::hstore - 2;
+SELECT '[a,b,c]'::hstore - 1;
+SELECT '[a,b,c]'::hstore - 0;
+SELECT '[a,b,c]'::hstore - -1;
+SELECT '[a,b,c]'::hstore - -2;
+SELECT '[a,b,c]'::hstore - -3;
+SELECT '[a,b,c]'::hstore - -4;
+
+SELECT 'a=>1, b=>2, c=>3'::hstore - 3;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 2;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 1;
+SELECT 'a=>1, b=>2, c=>3'::hstore - 0;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -1;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -2;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -3;
+SELECT 'a=>1, b=>2, c=>3'::hstore - -4;
+
+--replace
+
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b,-1}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1,0}', '{1,2,3}');
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,NULL,0}', '{1,2,3}');
+
+--deep concat
+
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'n=>not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'n=>not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'not_null');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{not_null}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'b=>{3,4}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b}', '{3,4}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '{4,5}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '4=>5');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d}', '2=>{4,5}');
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{NULL,1}', '4=>5');
+SELECT concat_path('x'::hstore, '{}'::text[], 'a=>"1"':: hstore);
+
+--cast 
+
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::text)::hstore AS err;
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json)::hstore AS ok;
+
+--hvals
+
+SELECT q->'tags' FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore) AS q;
+
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+SELECT q FROM hvals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+
+--svals path
+
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q;
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q;
+
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+SELECT q FROM svals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q;
+
+--each
+
+SELECT * FROM each('a=>b, c=>cc'::hstore) AS q;
+SELECT * FROM each('[a, b, c, cc]'::hstore) AS q;
+SELECT * FROM each('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+
+SELECT * FROM each_hstore('a=>b, c=>cc'::hstore) AS q;
+SELECT * FROM each_hstore('[a, b, c, cc]'::hstore) AS q;
+SELECT * FROM each_hstore('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q;
+
+--decoration
+
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]]'::hstore AS h, '[a, {b=>c}, [c, d, e]]'::hstore AS a;
+
+SET hstore.pretty_print = true;
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]], e=>[1,2,3,4], f=>g, g=>j'::hstore AS h, 
+	   '[a, {b=>c, c=>d}, [c, d, e, [1,2], h, {f=>g, g=>f}]]'::hstore AS a;
+RESET hstore.pretty_print;
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore);
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, array_curly_braces := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, loose := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, array_curly_braces := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, root_hash_decorated := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, array_curly_braces := true );
+
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true );
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true, loose := true);
diff --git a/contrib/hstore/sql/types.sql b/contrib/hstore/sql/types.sql
new file mode 100644
index 0000000..b441f15
--- /dev/null
+++ b/contrib/hstore/sql/types.sql
@@ -0,0 +1,152 @@
+SELECT '"foo"=>true'::hstore;
+SELECT 'foo=>true'::hstore;
+SELECT '"true"=>true'::hstore;
+SELECT 'true=>true'::hstore;
+SELECT '"t"=>true'::hstore;
+SELECT 't=>true'::hstore;
+SELECT '"false"=>true'::hstore;
+SELECT 'false=>true'::hstore;
+SELECT '"f"=>true'::hstore;
+SELECT 'f=>true'::hstore;
+
+SELECT '"foo"=>false'::hstore;
+SELECT 'foo=>false'::hstore;
+SELECT '"false"=>false'::hstore;
+SELECT 'false=>false'::hstore;
+SELECT '"t"=>false'::hstore;
+SELECT 't=>false'::hstore;
+SELECT '"false"=>false'::hstore;
+SELECT 'false=>false'::hstore;
+SELECT '"f"=>false'::hstore;
+SELECT 'f=>false'::hstore;
+
+SELECT '"1"=>x'::hstore;
+SELECT '1=>x'::hstore;
+SELECT 'foo=>1'::hstore;
+SELECT 'foo=>1.'::hstore;
+SELECT 'foo=>1.0'::hstore;
+SELECT 'foo=>1.01'::hstore;
+SELECT 'foo=>1.01e'::hstore;
+SELECT 'foo=>1.01e1'::hstore;
+SELECT 'foo=>1.01e+1'::hstore;
+SELECT 'foo=>1.01e-1'::hstore;
+SELECT 'foo=>.1'::hstore;
+SELECT 'foo=>.1e'::hstore;
+SELECT 'foo=>.1e1'::hstore;
+SELECT 'foo=>.1e+1'::hstore;
+SELECT 'foo=>.1e-1'::hstore;
+SELECT 'foo=>0.1e-1'::hstore;
+SELECT 'foo=>00.1e-1'::hstore;
+
+SELECT 'foo=>+1'::hstore;
+SELECT 'foo=>+1.'::hstore;
+SELECT 'foo=>+1.0'::hstore;
+SELECT 'foo=>+1.01'::hstore;
+SELECT 'foo=>+1.01e'::hstore;
+SELECT 'foo=>+1.01e1'::hstore;
+SELECT 'foo=>+1.01e+1'::hstore;
+SELECT 'foo=>+1.01e-1'::hstore;
+SELECT 'foo=>+.1'::hstore;
+SELECT 'foo=>+.1e'::hstore;
+SELECT 'foo=>+.1e1'::hstore;
+SELECT 'foo=>+.1e+1'::hstore;
+SELECT 'foo=>+.1e-1'::hstore;
+
+SELECT 'foo=>-1'::hstore;
+SELECT 'foo=>-1.'::hstore;
+SELECT 'foo=>-1.0'::hstore;
+SELECT 'foo=>-1.01'::hstore;
+SELECT 'foo=>-1.01e'::hstore;
+SELECT 'foo=>-1.01e1'::hstore;
+SELECT 'foo=>-1.01e+1'::hstore;
+SELECT 'foo=>-1.01e-1'::hstore;
+SELECT 'foo=>-.1'::hstore;
+SELECT 'foo=>-.1e'::hstore;
+SELECT 'foo=>-.1e1'::hstore;
+SELECT 'foo=>-.1e+1'::hstore;
+SELECT 'foo=>-.1e-1'::hstore;
+
+SELECT 'foo=>1e2000'::hstore;
+
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'foo';
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'bar';
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 0;
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 1;
+
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'foo';
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'bar';
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 0;
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 1;
+
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 0}';
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 1}';
+
+SELECT 'foo=>t, bar=>x'::hstore ?> 'foo';
+SELECT 'foo=>t, bar=>x'::hstore ?> 'bar';
+SELECT 'foo=>t, bar=>x'::hstore ?> 0;
+SELECT 'foo=>t, bar=>x'::hstore ?> 1;
+
+SELECT '[foo, t, bar, x]'::hstore ?> 'foo';
+SELECT '[foo, t, bar, x]'::hstore ?> 'bar';
+SELECT '[foo, t, bar, x]'::hstore ?> 0;
+SELECT '[foo, t, bar, x]'::hstore ?> 1;
+
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 0}';
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 1}';
+
+SELECT 'foo=>f, bar=>x'::hstore ?> 'foo';
+SELECT 'foo=>f, bar=>x'::hstore ?> 'bar';
+SELECT 'foo=>f, bar=>x'::hstore ?> 0;
+SELECT 'foo=>f, bar=>x'::hstore ?> 1;
+
+SELECT '[foo, f, bar, x]'::hstore ?> 'foo';
+SELECT '[foo, f, bar, x]'::hstore ?> 'bar';
+SELECT '[foo, f, bar, x]'::hstore ?> 0;
+SELECT '[foo, f, bar, x]'::hstore ?> 1;
+
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 0}';
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 1}';
+
+
+SELECT hstore_typeof('a=>b') AS hash;
+SELECT hstore_typeof('{a=>b}') AS hash;
+SELECT hstore_typeof('{a, b}') AS array;
+SELECT hstore_typeof('{{a=>b}}') AS array;
+SELECT hstore_typeof('[a, b]') AS array;
+SELECT hstore_typeof('') AS "NULL";
+SELECT hstore_typeof('NULL') AS "null";
+SELECT hstore_typeof('1.0') AS numeric;
+SELECT hstore_typeof('t') AS bool;
+SELECT hstore_typeof('f') AS bool;
+
+SELECT hstore('xxx', 't'::bool);
+SELECT hstore('xxx', 'f'::bool);
+
+SELECT hstore('xxx', 3.14);
+SELECT hstore('xxx', 3.14::numeric);
+SELECT hstore('xxx', '3.14'::numeric);
+
+SELECT hstore(NULL);
+SELECT hstore('NULL');
+
+SELECT hstore('t'::bool) AS "true", hstore('f'::bool) AS "false";
+
+SELECT hstore(3.14), hstore(3.14::numeric), hstore('3.14'::numeric);
+
+SELECT hstore('xxx', 'foo=>t, bar=>3.14, zzz=>xxx'::hstore);
+
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int2[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int4[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int8[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float4[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float8[]);
+SELECT array_to_hstore('{{1,1,f},{f,t,NULL}}'::bool[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::text[]);
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::varchar[]);
+
+SELECT array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]);
+SELECT hstore('array', array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]));
+
+SELECT 'a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore;
+SELECT hstore_to_json('a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore);
+SELECT hstore_to_json_loose('a=>"00012333", b=>"12233", c=>00012333, d=>12233'::hstore);
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index e5b9e66..4b854f9 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -12,7 +12,7 @@
    <productname>PostgreSQL</> also provides event triggers.  Unlike regular
    triggers, which are attached to a single table and capture only DML events,
    event triggers are global to a particular database and are capable of
-   capturing DDL events.
+   capturing DDL events or transaction commits.
   </para>
 
   <para>
@@ -29,8 +29,9 @@
      occurs in the database in which it is defined. Currently, the only
      supported events are
      <literal>ddl_command_start</>,
-     <literal>ddl_command_end</>
-     and <literal>sql_drop</>.
+     <literal>ddl_command_end</>,
+     <literal>sql_drop</>, and
+     <literal>transaction_commit</>.
      Support for additional events may be added in future releases.
    </para>
 
@@ -65,6 +66,15 @@
    </para>
 
    <para>
+    A <literal>transaction_commit</> trigger is called at the end of a
+    transaction, just before any deferred triggers are fired, unless
+    no data changes have been made by the transaction, or
+    <productname>PostgreSQL</> is running in Single-User mode. This is so
+    that you can recover from a badly specified <literal>transaction_commit</>
+    trigger.
+   </para>
+
+   <para>
      Event triggers (like other functions) cannot be executed in an aborted
      transaction.  Thus, if a DDL command fails with an error, any associated
      <literal>ddl_command_end</> triggers will not be executed.  Conversely,
@@ -77,8 +87,13 @@
    </para>
 
    <para>
-     For a complete list of commands supported by the event trigger mechanism,
-     see <xref linkend="event-trigger-matrix">.
+    A <literal>transaction_commit</> trigger is also not called in an
+    aborted transaction.
+   </para>
+
+   <para>
+     For a complete list of commands supported by the event trigger
+     mechanism, see <xref linkend="event-trigger-matrix">.
    </para>
 
    <para>
@@ -101,6 +116,11 @@
      to intercept. A common use of such triggers is to restrict the range of
      DDL operations which users may perform.
    </para>
+
+   <para>
+    <literal>transaction_commit</> triggers do not currently support
+    <literal>WHEN</literal> clauses.
+   </para>
   </sect1>
 
   <sect1 id="event-trigger-matrix">
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index fbe9543..a4ebfcd 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -8,57 +8,170 @@
  </indexterm>
 
  <para>
-  This module implements the <type>hstore</> data type for storing sets of
-  key/value pairs within a single <productname>PostgreSQL</> value.
+  This module implements the <type>hstore</> data type for storing arbitrarily
+  nested key/value pairs and arrays 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 and values are
-  simply text strings.
+  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.
  </para>
 
  <sect2>
   <title><type>hstore</> External Representation</title>
 
   <para>
-
    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:
+   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:
 
-<synopsis>
-k =&gt; v
-foo =&gt; bar, baz =&gt; whatever
-"1-a" =&gt; "anything at all"
-</synopsis>
+<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>
 
    The order of the pairs is not significant (and may not be reproduced on
-   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.
+   output).
   </para>
 
   <para>
-   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:
+   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:
 
 <programlisting>
 SELECT 'a=&gt;1,a=&gt;2'::hstore;
   hstore
 ----------
- "a"=&gt;"1"
+ "a"=&gt;1
 </programlisting>
   </para>
 
   <para>
-   A value (but not a key) can be an SQL <literal>NULL</>. For example:
+   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>:
 
 <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>.
+=% 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>
   </para>
 
   <note>
@@ -83,6 +196,36 @@ key =&gt; NULL
  </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>
 
   <para>
@@ -98,6 +241,7 @@ key =&gt; NULL
     <thead>
      <row>
       <entry>Operator</entry>
+      <entry>Returns</entry>
       <entry>Description</entry>
       <entry>Example</entry>
       <entry>Result</entry>
@@ -107,20 +251,111 @@ key =&gt; NULL
     <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>
@@ -128,62 +363,103 @@ key =&gt; NULL
 
      <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>t</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>
      </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>t</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 any of the specified keys?</entry>
       <entry><literal>'a=&gt;1,b=&gt;2'::hstore ?| ARRAY['b','c']</literal></entry>
-      <entry><literal>t</literal></entry>
+      <entry><literal>true</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>t</literal></entry>
+      <entry><literal>true</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>f</literal></entry>
+      <entry><literal>false</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>
+      <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>
      </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>
+      <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>
      </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>
@@ -191,6 +467,7 @@ key =&gt; NULL
 
      <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>
@@ -198,6 +475,7 @@ key =&gt; NULL
 
      <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>
@@ -208,12 +486,22 @@ key =&gt; NULL
   </table>
 
   <note>
-  <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>
+    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>
   </note>
 
@@ -246,7 +534,7 @@ key =&gt; NULL
       <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>
@@ -258,6 +546,14 @@ key =&gt; NULL
      </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>
       <entry>make single-item <type>hstore</></entry>
@@ -266,6 +562,54 @@ key =&gt; NULL
      </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>
       <entry>get <type>hstore</>'s keys as an array</entry>
@@ -306,6 +650,18 @@ b
      </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>
       <entry>get <type>hstore</>'s keys and values as an array of alternating
@@ -327,7 +683,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>{"a key": "1", "b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "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>
@@ -335,7 +691,15 @@ 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>{"a key": 1, "b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 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>
      </row>
 
      <row>
@@ -343,7 +707,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>
@@ -361,6 +725,20 @@ b
      </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>
       <entry>does <type>hstore</> contain key?</entry>
@@ -377,11 +755,35 @@ b
      </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>
@@ -389,7 +791,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>
@@ -397,14 +799,22 @@ 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 Examples section</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></entry>
      </row>
 
@@ -415,7 +825,8 @@ b
   <note>
    <para>
      The function <function>hstore_to_json</function> is used when an <type>hstore</type>
-     value is cast to <type>json</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>.
    </para>
   </note>
 
@@ -426,6 +837,14 @@ 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>
@@ -441,6 +860,13 @@ 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>
    <type>hstore</> also supports <type>btree</> or <type>hash</> indexes for
    the <literal>=</> operator. This allows <type>hstore</> columns to be
@@ -458,6 +884,155 @@ CREATE INDEX hidx ON testhstore USING HASH (h);
  </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>
 
   <para>
@@ -483,21 +1058,7 @@ INSERT INTO test VALUES (123, 'foo', 'bar');
 SELECT hstore(t) FROM test AS t;
                    hstore                    
 ---------------------------------------------
- "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  | 
+ "col1"=&gt;123, "col2"=&gt;"foo", "col3"=&gt;"bar"
 (1 row)
 </programlisting>
   </para>
@@ -567,11 +1128,14 @@ SELECT key, count(*) FROM
  <sect2>
   <title>Compatibility</title>
 
-  <para>
-   As of PostgreSQL 9.0, <type>hstore</> uses a different internal
-   representation than previous versions. This presents no obstacle for
+  <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
    dump/restore upgrades since the text representation (used in the dump) is
-   unchanged.
+   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.
   </para>
 
   <para>
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 356890d..f41d6fc 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -203,6 +203,7 @@ distprep:
 	$(MAKE) -C catalog	schemapg.h postgres.bki postgres.description postgres.shdescription
 	$(MAKE) -C replication	repl_gram.c repl_scanner.c
 	$(MAKE) -C utils	fmgrtab.c fmgroids.h errcodes.h
+	$(MAKE) -C utils/adt	jsonb_gram.c jsonb_scan.c
 	$(MAKE) -C utils/misc	guc-file.c
 	$(MAKE) -C utils/sort	qsort_tuple.c
 
@@ -320,6 +321,9 @@ maintainer-clean: distclean
 	      utils/fmgroids.h \
 	      utils/fmgrtab.c \
 	      utils/errcodes.h \
+	      utils/adt/jsonb_gram.c \
+	      utils/adt/jsonb_gram.h \
+	      utils/adt/jsonb_scan.c \
 	      utils/misc/guc-file.c \
 	      utils/sort/qsort_tuple.c
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 043d118..ca6d14c 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -796,3 +796,11 @@ CREATE OR REPLACE FUNCTION
 CREATE OR REPLACE FUNCTION
   json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
   RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'json_populate_recordset';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_record(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS anyelement LANGUAGE internal STABLE AS 'jsonb_populate_record';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_populate_recordset(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
+  RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100  AS 'jsonb_populate_recordset';
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 1ae9fa0..fd93d9b 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -32,7 +32,8 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
 	tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
 	tsvector.o tsvector_op.o tsvector_parser.o \
 	txid.o uuid.o windowfuncs.o xml.o rangetypes_spgist.o \
-	rangetypes_typanalyze.o rangetypes_selfuncs.o
+	rangetypes_typanalyze.o rangetypes_selfuncs.o \
+	jsonb.o jsonb_support.o
 
 like.o: like.c like_match.c
 
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 21a2336..421049c 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -1259,7 +1259,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
 			pfree(outputstr);
 			break;
 		case TYPCATEGORY_JSON:
-			/* JSON will already be escaped */
+			/* JSON and JSONB will already be escaped */
 			outputstr = OidOutputFunctionCall(typoutputfunc, val);
 			appendStringInfoString(result, outputstr);
 			pfree(outputstr);
@@ -1387,7 +1387,7 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
 		tcategory = TYPCATEGORY_JSON_CAST;
 	else if (element_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (element_type == JSONOID)
+	else if (element_type == JSONOID || element_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(element_type);
@@ -1482,7 +1482,8 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
 			tcategory = TYPCATEGORY_ARRAY;
 		else if (tupdesc->attrs[i]->atttypid == RECORDOID)
 			tcategory = TYPCATEGORY_COMPOSITE;
-		else if (tupdesc->attrs[i]->atttypid == JSONOID)
+		else if (tupdesc->attrs[i]->atttypid == JSONOID || 
+				 tupdesc->attrs[i]->atttypid == JSONBOID)
 			tcategory = TYPCATEGORY_JSON;
 		else
 			tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
@@ -1608,7 +1609,7 @@ to_json(PG_FUNCTION_ARGS)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (val_type == JSONOID)
+	else if (val_type == JSONOID || val_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(val_type);
@@ -1702,7 +1703,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
-	else if (val_type == JSONOID)
+	else if (val_type == JSONOID || val_type == JSONBOID)
 		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(val_type);
@@ -1804,12 +1805,15 @@ escape_json(StringInfo buf, const char *str)
 Datum
 json_typeof(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonTokenType tok;
 	char *type;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
+
 	/* Lex exactly one token from the input and check its type. */
 	json_lex(lex);
 	tok = lex_peek(lex);
@@ -1840,3 +1844,4 @@ json_typeof(PG_FUNCTION_ARGS)
 
 	PG_RETURN_TEXT_P(cstring_to_text(type));
 }
+
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
new file mode 100644
index 0000000..2b34a93
--- /dev/null
+++ b/src/backend/utils/adt/jsonb.c
@@ -0,0 +1,565 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.c
+ *		I/O for jsonb type 
+ *
+ * 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
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/json.h"
+#include "utils/jsonapi.h"
+#include "utils/jsonb.h"
+
+static size_t
+checkStringLen(size_t len)
+{
+	 if (len > JSONB_MAX_STRING_LEN)
+		  ereport(ERROR,
+					 (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
+					  errmsg("string too long for jsonb string")));
+	 return len;
+}
+
+static Jsonb*
+dumpJsonb(JsonbValue *p)
+{
+	 uint32			 buflen;
+	 Jsonb			*out;
+
+	 if (p == NULL)
+	 {
+		  buflen = 0;
+		  out = palloc(VARHDRSZ);
+	 }
+	 else
+	 {
+		  buflen = VARHDRSZ + p->size;
+		  out = palloc(buflen);
+		  SET_VARSIZE(out, buflen);
+
+		  buflen = compressJsonb(p, VARDATA(out));
+	 }
+	 SET_VARSIZE(out, buflen + VARHDRSZ);
+
+	 return out;
+}
+
+typedef struct JsonbInState
+{
+	ToJsonbState	*state;
+	JsonbValue		*res;
+}	JsonbInState;
+
+
+/*
+ * for jsonb we always want the de-escaped value - that's what's in token 
+ */
+
+static void 
+jsonb_in_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+	JsonbValue		v;
+
+	v.size = sizeof(JEntry);
+
+	switch (tokentype)
+	{
+			
+	case JSON_TOKEN_STRING:
+		v.type = jbvString;
+		v.string.len = token ? checkStringLen(strlen(token)) : 0;
+		v.string.val = token ? pnstrdup(token, v.string.len) : NULL;
+		v.size += v.string.len;
+		break;
+	case JSON_TOKEN_NUMBER:
+		v.type = jbvNumeric;
+		v.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
+		v.size += VARSIZE_ANY(v.numeric) + sizeof(JEntry) /* alignment */;
+		break;
+	case JSON_TOKEN_TRUE:
+		v.type = jbvBool;
+		v.boolean = true;
+		break;
+	case JSON_TOKEN_FALSE:
+		v.type = jbvBool;
+		v.boolean = false;
+		break;
+	case JSON_TOKEN_NULL:
+		v.type = jbvNull;
+		break;
+	default: /* nothing else should be here in fact */
+		break;
+	}
+
+	if (_state->state == NULL)
+	{
+		/* single scalar */
+		JsonbValue	va;
+
+		va.type = jbvArray;
+		va.array.scalar = true;
+		va.array.nelems = 1;
+
+		_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, &va);
+		_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+		_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+	}
+	else
+	{
+		JsonbValue	*o = &_state->state->v;
+
+		switch(o->type)
+		{
+			case jbvArray:
+				_state->res = pushJsonbValue(&_state->state, WJB_ELEM, &v);
+				break;
+			case jbvHash:
+				_state->res = pushJsonbValue(&_state->state, WJB_VALUE, &v);
+				break;
+			default:
+				elog(ERROR, "Wrong state");
+		}
+	}
+}
+
+static void
+jsonb_in_object_start(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_OBJECT, NULL);
+}
+
+static void
+jsonb_in_object_end(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_OBJECT, NULL);
+}
+
+static void
+jsonb_in_array_start(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_BEGIN_ARRAY, NULL);
+}
+
+static void
+jsonb_in_array_end(void *state)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_END_ARRAY, NULL);
+}
+
+static void
+jsonb_in_object_field_start(void *state, char *fname, bool isnull)
+{
+	JsonbInState 	*_state = (JsonbInState *) state;
+	JsonbValue		v;
+
+	v.type = jbvString;
+	v.string.len = fname ? checkStringLen(strlen(fname)) : 0;
+	v.string.val = fname ? pnstrdup(fname, v.string.len) : NULL;
+	v.size = sizeof(JEntry) + v.string.len;
+
+	_state->res = pushJsonbValue(&_state->state, WJB_KEY, &v);
+}
+
+Datum
+jsonb_in(PG_FUNCTION_ARGS)
+{
+	char	   		*json = PG_GETARG_CSTRING(0);
+	text	   		*result = cstring_to_text(json);
+	JsonLexContext 	*lex;
+	JsonbInState 	state;
+	JsonSemAction 	sem;
+
+	memset(&state, 0, sizeof(state));
+	memset(&sem, 0, sizeof(sem));
+	lex = makeJsonLexContext(result, true);
+
+	sem.semstate = (void *) &state;
+
+	sem.object_start = jsonb_in_object_start;
+	sem.array_start = jsonb_in_array_start;
+	sem.object_end = jsonb_in_object_end;
+	sem.array_end = jsonb_in_array_end;
+	sem.scalar = jsonb_in_scalar;
+	sem.object_field_start = jsonb_in_object_field_start;
+
+	pg_parse_json(lex, &sem);
+
+	/* after parsing, the item membar has the composed jsonn structure */
+	PG_RETURN_POINTER(dumpJsonb(state.res));
+}
+
+static void recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header);
+
+static void
+recvJsonbValue(StringInfo buf, JsonbValue *v, uint32 level, int c)
+{
+	 uint32  hentry = c & JENTRY_TYPEMASK;
+
+	 if (hentry == JENTRY_ISNULL)
+	 {
+		  v->type = jbvNull;
+		  v->size = sizeof(JEntry);
+	 }
+	 else if (hentry == JENTRY_ISOBJECT || hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	 {
+		  recvJsonb(buf, v, level + 1, (uint32)c);
+	 }
+	 else if (hentry == JENTRY_ISFALSE || hentry == JENTRY_ISTRUE)
+	 {
+		  v->type = jbvBool;
+		  v->size = sizeof(JEntry);
+		  v->boolean = (hentry == JENTRY_ISFALSE) ? false : true;
+	 }
+	 else if (hentry == JENTRY_ISNUMERIC)
+	 {
+		  v->type = jbvNumeric;
+		  v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv, PointerGetDatum(buf),
+																			Int32GetDatum(0), Int32GetDatum(-1)));
+		  v->size = sizeof(JEntry) * 2 + VARSIZE_ANY(v->numeric);
+	 }
+	 else if (hentry == JENTRY_ISSTRING)
+	 {
+		  v->type = jbvString;
+		  v->string.val = pq_getmsgtext(buf, c, &c);
+		  v->string.len = checkStringLen(c);
+		  v->size = sizeof(JEntry) + v->string.len;
+	 }
+	 else
+	 {
+		  elog(ERROR, "bogus input");
+	 }
+}
+
+static void
+recvJsonb(StringInfo buf, JsonbValue *v, uint32 level, uint32 header)
+{
+	 uint32  hentry;
+	 uint32  i;
+
+	 hentry = header & JENTRY_TYPEMASK;
+
+	 v->size = 3 * sizeof(JEntry);
+	 if (hentry == JENTRY_ISOBJECT)
+	 {
+		  v->type = jbvHash;
+		  v->hash.npairs = header & JB_COUNT_MASK;
+		  if (v->hash.npairs > 0)
+		  {
+				v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs);
+
+				for(i=0; i<v->hash.npairs; i++)
+				{
+					 recvJsonbValue(buf, &v->hash.pairs[i].key, level, pq_getmsgint(buf, 4));
+					 if (v->hash.pairs[i].key.type != jbvString)
+						  elog(ERROR, "jsonb's key could be only a string");
+
+					 recvJsonbValue(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;
+				}
+
+				uniqueJsonbValue(v);
+		  }
+	 }
+	 else if (hentry == JENTRY_ISARRAY || hentry == JENTRY_ISCALAR)
+	 {
+		  v->type = jbvArray;
+		  v->array.nelems = header & JB_COUNT_MASK;
+		  v->array.scalar = (hentry == JENTRY_ISCALAR) ? true : false;
+
+		  if (v->array.scalar && v->array.nelems != 1)
+				elog(ERROR, "bogus input");
+
+		  if (v->array.nelems > 0)
+		  {
+				v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems);
+
+				for(i=0; i<v->array.nelems; i++)
+				{
+					 recvJsonbValue(buf, v->array.elems + i, level, pq_getmsgint(buf, 4));
+					 v->size += v->array.elems[i].size;
+				}
+		  }
+	 }
+	 else
+	 {
+				elog(ERROR, "bogus input");
+	 }
+}
+
+Datum
+jsonb_recv(PG_FUNCTION_ARGS)
+{
+	 StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
+	 JsonbValue v;
+
+	 recvJsonb(buf, &v, 0, pq_getmsgint(buf, 4));
+
+	 PG_RETURN_POINTER(dumpJsonb(&v));
+}
+
+static void
+putEscapedValue(StringInfo out, JsonbValue *v)
+{
+	 switch(v->type)
+	 {
+		  case jbvNull:
+				appendBinaryStringInfo(out, "null", 4);
+				break;
+		  case jbvString:
+				escape_json(out, pnstrdup(v->string.val, v->string.len));
+				break;
+		  case jbvBool:
+				if (v->boolean)
+					 appendBinaryStringInfo(out, "true", 4);
+				else
+					 appendBinaryStringInfo(out, "false", 5);
+				break;
+		  case jbvNumeric:
+				appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))));
+				break;
+		  default:
+				elog(PANIC, "Unknown type");
+	 }
+}
+
+char*
+JsonbToCString(StringInfo out, char *in, int estimated_len)
+{
+	 bool			first = true;
+	 JsonbIterator  *it;
+	 int			type;
+	 JsonbValue	  	v;
+	 int			 level = 0;
+
+	 if (out == NULL)
+		  out = makeStringInfo();
+
+	 if (in == NULL)
+	 {
+		  appendStringInfoString(out, "");
+		  return out->data;
+	 }
+
+	 enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);
+
+	 it = JsonbIteratorInit(in);
+
+	 while((type = JsonbIteratorGet(&it, &v, false)) != 0)
+	 {
+reout:
+		  switch(type)
+		  {
+				case WJB_BEGIN_ARRAY:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+
+					 if (v.array.scalar == false)
+						 appendStringInfoChar(out, '[');
+					 level++;
+					 break;
+				case WJB_BEGIN_OBJECT:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+					 appendStringInfoCharMacro(out, '{');
+
+					 level++;
+					 break;
+				case WJB_KEY:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 first = true;
+
+					 putEscapedValue(out, &v);
+					 appendBinaryStringInfo(out, ": ", 2);
+
+					 type = JsonbIteratorGet(&it, &v, false);
+					 if (type == WJB_VALUE)
+					 {
+						  first = false;
+						  putEscapedValue(out, &v);
+					 }
+					 else
+					 {
+						  Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
+						  goto reout;
+					 }
+					 break;
+				case WJB_ELEM:
+					 if (first == false)
+						  appendBinaryStringInfo(out, ", ", 2);
+					 else
+						  first = false;
+
+					 putEscapedValue(out, &v);
+					 break;
+				case WJB_END_ARRAY:
+					 level--;
+					 if (v.array.scalar == false)
+						  appendStringInfoChar(out, ']');
+					 first = false;
+					 break;
+				case WJB_END_OBJECT:
+					 level--;
+					 appendStringInfoCharMacro(out, '}');
+					 first = false;
+					 break;
+				default:
+					 elog(PANIC, "Wrong flags");
+		  }
+	 }
+
+	 Assert(level == 0);
+
+	 return out->data;
+}
+
+Datum
+jsonb_out(PG_FUNCTION_ARGS)
+{
+	 Jsonb  *jb = PG_GETARG_JSONB(0);
+	 char	 *out;
+
+	 out = JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb));
+
+	 PG_RETURN_CSTRING(out);
+}
+
+Datum
+jsonb_send(PG_FUNCTION_ARGS)
+{
+	 Jsonb			 *in = PG_GETARG_JSONB(0);
+	 StringInfoData  buf;
+
+	 pq_begintypsend(&buf);
+
+	 if (JB_ISEMPTY(in))
+	 {
+		  pq_sendint(&buf, 0, 4);
+	 }
+	 else
+	 {
+		  JsonbIterator  *it;
+		  int				 type;
+		  JsonbValue	  v;
+		  uint32			 flag;
+		  bytea			  *nbuf;
+
+		  enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */);
+
+		  it = JsonbIteratorInit(VARDATA_ANY(in));
+
+		  while((type = JsonbIteratorGet(&it, &v, false)) != 0)
+		  {
+				switch(type)
+				{
+					 case WJB_BEGIN_ARRAY:
+						  flag = (v.array.scalar) ? JENTRY_ISCALAR : JENTRY_ISARRAY;
+						  pq_sendint(&buf, v.array.nelems | flag, 4);
+						  break;
+					 case WJB_BEGIN_OBJECT:
+						  pq_sendint(&buf, v.hash.npairs | JENTRY_ISOBJECT, 4);
+						  break;
+					 case WJB_KEY:
+						  pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+						  pq_sendtext(&buf, v.string.val, v.string.len);
+						  break;
+					 case WJB_ELEM:
+					 case WJB_VALUE:
+						  switch(v.type)
+						  {
+								case jbvNull:
+									 pq_sendint(&buf, JENTRY_ISNULL, 4);
+									 break;
+								case jbvString:
+									 pq_sendint(&buf, v.string.len | JENTRY_ISSTRING, 4);
+									 pq_sendtext(&buf, v.string.val, v.string.len);
+									 break;
+								case jbvBool:
+									 pq_sendint(&buf, (v.boolean) ? JENTRY_ISTRUE : JENTRY_ISFALSE, 4);
+									 break;
+								case jbvNumeric:
+									 nbuf = DatumGetByteaP(DirectFunctionCall1(numeric_send, NumericGetDatum(v.numeric)));
+									 pq_sendint(&buf, VARSIZE_ANY(nbuf) | JENTRY_ISNUMERIC, 4);
+									 pq_sendbytes(&buf, (char*)nbuf, VARSIZE_ANY(nbuf));
+									 break;
+								default:
+									 elog(PANIC, "Wrong type: %u", v.type);
+						  }
+						  break;
+					 case WJB_END_ARRAY:
+					 case WJB_END_OBJECT:
+						  break;
+					 default:
+						  elog(PANIC, "Wrong flags");
+				}
+		  }
+	 }
+
+	 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+Datum
+jsonb_typeof(PG_FUNCTION_ARGS)
+{
+	 Jsonb			 *in = PG_GETARG_JSONB(0);
+	 JsonbIterator   *it;
+	 JsonbValue	      v;
+	 char            *result;
+
+	 if (JB_ROOT_IS_OBJECT(in))
+		 result = "object";
+	 else if (JB_ROOT_IS_ARRAY(in) && ! JB_ROOT_IS_SCALAR(in))
+		 result = "array";
+	 else
+	 {
+		 Assert(JB_ROOT_IS_SCALAR(in));
+
+		 it = JsonbIteratorInit(VARDATA_ANY(in));
+		 /* 
+		  * a root scalar is stored as an array of one element, 
+		  * so we get the array and then its first (and only) member.
+		  */
+		 (void) JsonbIteratorGet(&it, &v, true);
+		 (void) JsonbIteratorGet(&it, &v, true);
+		 switch(v.type)
+		 {
+		 case jbvNull:
+			 result = "null";
+			 break;
+		 case jbvString:
+			 result = "string";
+			 break;
+		 case jbvBool:
+			 result = "boolean";
+			 break;
+		 case jbvNumeric:
+			 result = "number";
+			 break;
+		 default:
+			 elog(ERROR, "Wrong jsonb scalar type: %u", v.type);
+		 }
+	 }
+	 
+	PG_RETURN_TEXT_P(cstring_to_text(result));		 
+}
diff --git a/src/backend/utils/adt/jsonb_support.c b/src/backend/utils/adt/jsonb_support.c
new file mode 100644
index 0000000..ddc96a3
--- /dev/null
+++ b/src/backend/utils/adt/jsonb_support.c
@@ -0,0 +1,1180 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb_support.c
+ *    Support functions for jsonb
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/adt/jsonb_support.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "utils/builtins.h"
+#include "utils/jsonb.h"
+
+/* turn a JsonbValue into a Jsonb */
+
+Jsonb *
+JsonbValueToJsonb(JsonbValue *v)
+{
+	Jsonb			*out;
+
+	if (v == NULL)
+	{
+		out = NULL;
+	}
+	else if (v->type == jbvString || v->type == jbvBool ||
+			 v->type == jbvNumeric || v->type == jbvNull)
+	{
+		ToJsonbState	*state = NULL;
+		JsonbValue		*res;
+		int				r;
+		JsonbValue		scalarArray;
+
+		scalarArray.type = jbvArray;
+		scalarArray.array.scalar = true;
+		scalarArray.array.nelems = 1;
+
+		pushJsonbValue(&state, WJB_BEGIN_ARRAY, &scalarArray);
+		pushJsonbValue(&state, WJB_ELEM, v);
+		res = pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+		out = palloc(VARHDRSZ + res->size);
+		SET_VARSIZE(out, VARHDRSZ + res->size);
+		r = compressJsonb(res, VARDATA(out));
+		Assert(r <= res->size);
+		SET_VARSIZE(out, r + VARHDRSZ);
+	}
+	else
+	{
+		out = palloc(VARHDRSZ + v->size);
+
+		Assert(v->type == jbvBinary);
+		SET_VARSIZE(out, VARHDRSZ + v->binary.len);
+		memcpy(VARDATA(out), v->binary.data, v->binary.len);
+	}
+
+	return out;
+}
+
+/*
+ * Sort and unique pairs in hash-like JsonbValue
+ */
+void
+uniqueJsonbValue(JsonbValue *v)
+{
+	bool    hasNonUniq = false;
+
+	Assert(v->type == jbvHash);
+
+	if (v->hash.npairs > 1)
+		qsort_arg(v->hash.pairs, v->hash.npairs, sizeof(*v->hash.pairs),
+				  compareJsonbPair, &hasNonUniq);
+
+	if (hasNonUniq)
+	{
+		JsonbPair	*ptr = v->hash.pairs + 1,
+					*res = v->hash.pairs;
+
+		while(ptr - v->hash.pairs < v->hash.npairs)
+		{
+			if (ptr->key.string.len == res->key.string.len &&
+				memcmp(ptr->key.string.val, res->key.string.val,
+				ptr->key.string.len) == 0)
+			{
+				v->size -= ptr->key.size + ptr->value.size;
+			}
+			else
+			{
+				res++;
+				if (ptr != res)
+					memcpy(res, ptr, sizeof(*res));
+			}
+			ptr++;
+		}
+
+		v->hash.npairs = res + 1 - v->hash.pairs;
+	}
+}
+
+/****************************************************************************
+ *                         Compare Functions                                *
+ ****************************************************************************/
+int
+compareJsonbStringValue(const void *a, const void *b, void *arg)
+{
+	const JsonbValue  *va = a;
+	const JsonbValue  *vb = b;
+	int					res;
+
+	Assert(va->type == jbvString);
+	Assert(vb->type == jbvString);
+
+	if (va->string.len == vb->string.len)
+	{
+		res = memcmp(va->string.val, vb->string.val, va->string.len);
+		if (res == 0 && arg)
+			*(bool*)arg = true;
+	}
+	else
+	{
+		res = (va->string.len > vb->string.len) ? 1 : -1;
+	}
+
+	return res;
+}
+
+int
+compareJsonbPair(const void *a, const void *b, void *arg)
+{
+	const 	JsonbPair *pa = a;
+	const 	JsonbPair *pb = b;
+	int 	res;
+
+	res = compareJsonbStringValue(&pa->key, &pb->key, arg);
+
+	/*
+	 * guarantee keeping order of equal pair. Unique algorithm will
+	 * prefer first element as value
+	 */
+
+	if (res == 0)
+		res = (pa->order > pb->order) ? -1 : 1;
+
+	return res;
+}
+
+int
+compareJsonbValue(JsonbValue *a, JsonbValue *b)
+{
+	if (a->type == b->type)
+	{
+		switch(a->type)
+		{
+			case jbvNull:
+				return 0;
+			case jbvString:
+				return compareJsonbStringValue(a, b, NULL);
+			case jbvBool:
+				if (a->boolean == b->boolean)
+					return 0;
+				return (a->boolean > b->boolean) ? 1 : -1;
+			case jbvNumeric:
+				return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+														 PointerGetDatum(a->numeric),
+														 PointerGetDatum(b->numeric)));
+			case jbvArray:
+				if (a->array.nelems == b->array.nelems)
+				{
+					int i, r;
+
+					for(i=0; i<a->array.nelems; i++)
+						if ((r = compareJsonbValue(a->array.elems + i, 
+												   b->array.elems + i)) != 0)
+							return r;
+
+					return 0;
+				}
+
+				return (a->array.nelems > b->array.nelems) ? 1 : -1;
+			case jbvHash:
+				if (a->hash.npairs == b->hash.npairs)
+				{
+					int i, r;
+
+					for(i=0; i<a->hash.npairs; i++)
+					{
+						if ((r = compareJsonbStringValue(&a->hash.pairs[i].key,
+														 &b->hash.pairs[i].key,
+														 NULL)) != 0)
+							return r;
+						if ((r = compareJsonbValue(&a->hash.pairs[i].value, 
+												   &b->hash.pairs[i].value)) != 0)
+							return r;
+					}
+
+					return 0;
+				}
+
+				return (a->hash.npairs > b->hash.npairs) ? 1 : -1;
+			case jbvBinary:
+				return compareJsonbBinaryValue(a->binary.data, b->binary.data);
+			default:
+				elog(PANIC, "unknown JsonbValue->type: %d", a->type);
+		}
+	}
+
+	return (a->type > b->type) ? 1 : -1;
+}
+
+int
+compareJsonbBinaryValue(char *a, char *b)
+{
+	JsonbIterator	*it1, *it2;
+	int				res = 0;
+
+	it1 = JsonbIteratorInit(a);
+	it2 = JsonbIteratorInit(b);
+
+	while(res == 0)
+	{
+		JsonbValue		v1, v2;
+		int				r1, r2;
+
+		r1 = JsonbIteratorGet(&it1, &v1, false);
+		r2 = JsonbIteratorGet(&it2, &v2, false);
+
+		if (r1 == r2)
+		{
+			if (r1 == 0)
+				break; /* equal */
+
+			if (v1.type == v2.type)
+			{
+				switch(v1.type)
+				{
+					case jbvString:
+						res = compareJsonbStringValue(&v1, &v2, NULL);
+						break;
+					case jbvBool:
+						if (v1.boolean == v2.boolean)
+							res = 0;
+						else
+							res = (v1.boolean > v2.boolean) ? 1 : -1;
+						break;
+					case jbvNumeric:
+						res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+												 PointerGetDatum(v1.numeric),
+												 PointerGetDatum(v2.numeric)));
+						break;
+					case jbvArray:
+						if (v1.array.nelems != v2.array.nelems)
+							res = (v1.array.nelems > v2.array.nelems) ? 1 : -1;
+						break;
+					case jbvHash:
+						if (v1.hash.npairs != v2.hash.npairs)
+							res = (v1.hash.npairs > v2.hash.npairs) ? 1 : -1;
+						break;
+					default:
+						break;
+				}
+			}
+			else
+			{
+				res = (v1.type > v2.type) ?  1 : -1; /* dummy order */
+			}
+		}
+		else
+		{
+			res = (r1 > r2) ? 1 : -1; /* dummy order */
+		}
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *                         find string key in hash or array                 *
+ ****************************************************************************/
+JsonbValue*
+findUncompressedJsonbValueByValue(char *buffer, uint32 flags, uint32 *lowbound, JsonbValue *key)
+{
+	uint32				header = *(uint32*)buffer;
+	static JsonbValue 	r;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) != 
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		JEntry	*array = (JEntry*)(buffer + sizeof(header));
+		char 	*data = (char*)(array + (header & JB_COUNT_MASK));
+		int 	i;
+
+		for(i=(lowbound) ? *lowbound : 0; i<(header & JB_COUNT_MASK); i++) {
+			JEntry	*e = array + i;
+
+			if (JBE_ISNULL(*e) && key->type == jbvNull)
+			{
+				r.type = jbvNull;
+				if (lowbound)
+					*lowbound = i;
+				r.size = sizeof(JEntry);
+
+				return &r;
+			}
+			else if (JBE_ISSTRING(*e) && key->type == jbvString )
+			{
+				if (key->string.len == JBE_LEN(*e) &&
+					memcmp(key->string.val, data + JBE_OFF(*e), 
+						   key->string.len) == 0)
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*e);
+					r.string.len = key->string.len;
+					r.size = sizeof(JEntry) + r.string.len;
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISBOOL(*e) && key->type == jbvBool)
+			{
+				if ((JBE_ISBOOL_TRUE(*e) && key->boolean == true) ||
+					(JBE_ISBOOL_FALSE(*e) && key->boolean == false))
+				{
+					r = *key;
+					r.size = sizeof(JEntry);
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+			else if (JBE_ISNUMERIC(*e) && key->type == jbvNumeric)
+			{
+				if (DatumGetBool(DirectFunctionCall2(numeric_eq, 
+								 PointerGetDatum(data + INTALIGN(JBE_OFF(*e))),
+								 PointerGetDatum(key->numeric))) == true)
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*e)));
+					if (lowbound)
+						*lowbound = i;
+
+					return &r;
+				}
+			}
+		}
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		JEntry  *array = (JEntry*)(buffer + sizeof(header));
+		char    *data = (char*)(array + (header & JB_COUNT_MASK) * 2);
+		uint32	stopLow = lowbound ? *lowbound : 0,
+				stopHigh = (header & JB_COUNT_MASK),
+				stopMiddle;
+
+		if (key->type != jbvString)
+			return NULL;
+
+		while (stopLow < stopHigh)
+		{
+			int		difference;
+			JEntry	*e;
+
+			stopMiddle = stopLow + (stopHigh - stopLow) / 2;
+
+			e = array + stopMiddle * 2;
+
+			if (key->string.len == JBE_LEN(*e))
+				difference = memcmp(data + JBE_OFF(*e), key->string.val,
+									key->string.len);
+			else
+				difference = (JBE_LEN(*e) > key->string.len) ? 1 : -1;
+
+			if (difference == 0)
+			{
+				JEntry	*v = e + 1;
+
+				if (lowbound)
+					*lowbound = stopMiddle + 1;
+
+				if (JBE_ISSTRING(*v))
+				{
+					r.type = jbvString;
+					r.string.val = data + JBE_OFF(*v);
+					r.string.len = JBE_LEN(*v);
+					r.size = sizeof(JEntry) + r.string.len;
+				}
+				else if (JBE_ISBOOL(*v))
+				{
+					r.type = jbvBool;
+					r.boolean = (JBE_ISBOOL_TRUE(*v)) ? true : false;
+					r.size = sizeof(JEntry);
+				}
+				else if (JBE_ISNUMERIC(*v))
+				{
+					r.type = jbvNumeric;
+					r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*v)));
+					r.size = 2*sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+				}
+				else if (JBE_ISNULL(*v))
+				{
+					r.type = jbvNull;
+					r.size = sizeof(JEntry);
+				}
+				else
+				{
+					r.type = jbvBinary;
+					r.binary.data = data + INTALIGN(JBE_OFF(*v));
+					r.binary.len = JBE_LEN(*v) - 
+									(INTALIGN(JBE_OFF(*v)) - JBE_OFF(*v));
+					r.size = 2*sizeof(JEntry) + r.binary.len;
+				}
+
+				return &r;
+			}
+			else if (difference < 0)
+			{
+				stopLow = stopMiddle + 1;
+			}
+			else
+			{
+				stopHigh = stopMiddle;
+			}
+		}
+
+		if (lowbound)
+			*lowbound = stopLow;
+	}
+
+	return NULL;
+}
+
+JsonbValue*
+findUncompressedJsonbValue(char *buffer, uint32 flags, uint32 *lowbound,
+						   char *key, uint32 keylen)
+{
+	JsonbValue	v;
+
+	if (key == NULL)
+	{
+		v.type = jbvNull;
+	}
+	else
+	{
+		v.type = jbvString;
+		v.string.val = key;
+		v.string.len = keylen;
+	}
+
+	return findUncompressedJsonbValueByValue(buffer, flags, lowbound, &v);
+}
+
+JsonbValue*
+getJsonbValue(char *buffer, uint32 flags, int32 i)
+{
+	uint32				header = *(uint32*)buffer;
+	static JsonbValue	r;
+	JEntry				*array, *e;
+	char				*data;
+
+	Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) !=
+		   (JB_FLAG_ARRAY | JB_FLAG_OBJECT));
+
+	if (i >= 0)
+	{
+		if (i >= (header & JB_COUNT_MASK))
+			return NULL;
+	}
+	else
+	{
+		if (-i > (header & JB_COUNT_MASK))
+			return NULL;
+
+		i = (header & JB_COUNT_MASK) + i;
+	}
+
+	array = (JEntry*)(buffer + sizeof(header));
+
+	if (flags & JB_FLAG_ARRAY & header)
+	{
+		e = array + i;
+		data = (char*)(array + (header & JB_COUNT_MASK));
+	}
+	else if (flags & JB_FLAG_OBJECT & header)
+	{
+		e = array + i * 2 + 1;
+		data = (char*)(array + (header & JB_COUNT_MASK) * 2);
+	}
+	else
+	{
+		return NULL;
+	}
+
+	if (JBE_ISSTRING(*e))
+	{
+		r.type = jbvString;
+		r.string.val = data + JBE_OFF(*e);
+		r.string.len = JBE_LEN(*e);
+		r.size = sizeof(JEntry) + r.string.len;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		r.type = jbvBool;
+		r.boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		r.size = sizeof(JEntry);
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		r.type = jbvNumeric;
+		r.numeric = (Numeric)(data + INTALIGN(JBE_OFF(*e)));
+		r.size = 2*sizeof(JEntry) + VARSIZE_ANY(r.numeric);
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		r.type = jbvNull;
+		r.size = sizeof(JEntry);
+	}
+	else
+	{
+		r.type = jbvBinary;
+		r.binary.data = data + INTALIGN(JBE_OFF(*e));
+		r.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		r.size = r.binary.len + 2*sizeof(JEntry);
+	}
+
+	return &r;
+}
+
+/****************************************************************************
+ *                         Walk on tree representation of jsonb             *
+ ****************************************************************************/
+static void
+walkUncompressedJsonbDo(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg, uint32 level) 
+{
+	int i;
+
+	switch(v->type)
+	{
+		case jbvArray:
+			cb(cb_arg, v, WJB_BEGIN_ARRAY, level);
+			for(i=0; i<v->array.nelems; i++)
+			{
+				if (v->array.elems[i].type == jbvNull ||
+					v->array.elems[i].type == jbvString ||
+					v->array.elems[i].type == jbvBool ||
+					v->array.elems[i].type == jbvNumeric ||
+					v->array.elems[i].type == jbvBinary)
+					cb(cb_arg, v->array.elems + i, WJB_ELEM, level);
+				else
+					walkUncompressedJsonbDo(v->array.elems + i, cb, cb_arg,
+											level + 1);
+			}
+			cb(cb_arg, v, WJB_END_ARRAY, level);
+			break;
+		case jbvHash:
+			cb(cb_arg, v, WJB_BEGIN_OBJECT, level);
+
+			for(i=0; i<v->hash.npairs; i++)
+			{
+				cb(cb_arg, &v->hash.pairs[i].key, WJB_KEY, level);
+				
+				if (v->hash.pairs[i].value.type == jbvNull ||
+					v->hash.pairs[i].value.type == jbvString ||
+					v->hash.pairs[i].value.type == jbvBool ||
+					v->hash.pairs[i].value.type == jbvNumeric ||
+					v->hash.pairs[i].value.type == jbvBinary)
+					cb(cb_arg, &v->hash.pairs[i].value, WJB_VALUE, level);
+				else 
+					walkUncompressedJsonbDo(&v->hash.pairs[i].value, cb, cb_arg,
+											level + 1);
+			}
+
+			cb(cb_arg, v, WJB_END_OBJECT, level);
+			break;
+		default:
+			elog(PANIC, "impossible JsonbValue->type: %d", v->type);
+	}
+}
+
+void
+walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg)
+{
+	if (v)
+		walkUncompressedJsonbDo(v, cb, cb_arg, 0);
+}
+
+/****************************************************************************
+ *                         Iteration over binary jsonb                      *
+ ****************************************************************************/
+static void
+parseBuffer(JsonbIterator *it, char *buffer)
+{
+	uint32	header = *(uint32*)buffer;
+
+	it->type = header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT);
+	it->nelems = header & JB_COUNT_MASK;
+	it->buffer = buffer;
+
+
+	buffer += sizeof(uint32);
+	it->array = (JEntry*)buffer;
+
+	it->state = jbi_start;
+
+	switch(it->type)
+	{
+		case JB_FLAG_ARRAY:
+			it->data = buffer + it->nelems * sizeof(JEntry);
+			it->isScalar = (header & JB_FLAG_SCALAR) ? true : false;
+			Assert(it->isScalar == false || it->nelems == 1);
+			break;
+		case JB_FLAG_OBJECT:
+			it->data = buffer + it->nelems * sizeof(JEntry) * 2;
+			break;
+		default:
+			elog(PANIC, "impossible type: %08x", it->type);
+	}
+}
+
+JsonbIterator*
+JsonbIteratorInit(char *buffer)
+{
+	JsonbIterator	*it = palloc(sizeof(*it));
+
+	parseBuffer(it, buffer);
+	it->next = NULL;
+
+	return it;
+}
+
+static bool
+formAnswer(JsonbIterator **it, JsonbValue *v, JEntry *e, bool skipNested)
+{
+	if (JBE_ISSTRING(*e))
+	{
+		v->type = jbvString;
+		v->string.val = (*it)->data + JBE_OFF(*e);
+		v->string.len = JBE_LEN(*e);
+		v->size = sizeof(JEntry) + v->string.len;
+
+		return false;
+	}
+	else if (JBE_ISBOOL(*e))
+	{
+		v->type = jbvBool;
+		v->boolean = (JBE_ISBOOL_TRUE(*e)) ? true : false;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (JBE_ISNUMERIC(*e))
+	{
+		v->type = jbvNumeric;
+		v->numeric = (Numeric)((*it)->data + INTALIGN(JBE_OFF(*e)));
+		v->size = 2*sizeof(JEntry) + VARSIZE_ANY(v->numeric);
+
+		return false;
+	}
+	else if (JBE_ISNULL(*e))
+	{
+		v->type = jbvNull;
+		v->size = sizeof(JEntry);
+
+		return false;
+	}
+	else if (skipNested)
+	{
+		v->type = jbvBinary;
+		v->binary.data = (*it)->data + INTALIGN(JBE_OFF(*e));
+		v->binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+		v->size = v->binary.len + 2*sizeof(JEntry);
+
+		return false;
+	}
+	else
+	{
+		JsonbIterator *nit = palloc(sizeof(*nit));
+
+		parseBuffer(nit, (*it)->data + INTALIGN(JBE_OFF(*e)));
+		nit->next = *it;
+		*it = nit;
+
+		return true;
+	}
+}
+
+static JsonbIterator*
+up(JsonbIterator *it)
+{
+	JsonbIterator *v = it->next;
+
+	pfree(it);
+
+	return v;
+}
+
+int
+JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested)
+{
+	int res;
+
+	if (*it == NULL)
+		return 0;
+
+	/*
+	 * Encode all possible states by one integer. That's possible
+	 * because enum members of JsonbIterator->state uses different bits
+	 * than JB_FLAG_ARRAY/JB_FLAG_OBJECT. See definition of JsonbIterator
+	 */
+
+	switch((*it)->type | (*it)->state)
+	{
+		case JB_FLAG_ARRAY | jbi_start:
+			(*it)->state = jbi_elem;
+			(*it)->i = 0;
+			v->type = jbvArray;
+			v->array.nelems = (*it)->nelems;
+			res = WJB_BEGIN_ARRAY;
+			v->array.scalar = (*it)->isScalar;
+			break;
+		case JB_FLAG_ARRAY | jbi_elem:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_ARRAY;
+			}
+			else if (formAnswer(it, v, &(*it)->array[ (*it)->i++ ], skipNested))
+			{
+				res = JsonbIteratorGet(it, v, skipNested);
+			}
+			else
+			{
+				res = WJB_ELEM;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_start:
+			(*it)->state = jbi_key;
+			(*it)->i = 0;
+			v->type = jbvHash;
+			v->hash.npairs = (*it)->nelems;
+			res = WJB_BEGIN_OBJECT;
+			break;
+		case JB_FLAG_OBJECT | jbi_key:
+			if ((*it)->i >= (*it)->nelems)
+			{
+				*it = up(*it);
+				res = WJB_END_OBJECT;
+			}
+			else
+			{
+				formAnswer(it, v, &(*it)->array[ (*it)->i * 2 ], false);
+				(*it)->state = jbi_value;
+				res = WJB_KEY;
+			}
+			break;
+		case JB_FLAG_OBJECT | jbi_value:
+			(*it)->state = jbi_key;
+			if (formAnswer(it, v, &(*it)->array[ ((*it)->i++) * 2 + 1], skipNested))
+				res = JsonbIteratorGet(it, v, skipNested);
+			else
+				res = WJB_VALUE;
+			break;
+		default:
+			elog(PANIC,"unknown state %08x", (*it)->type & (*it)->state);
+	}
+
+	return res;
+}
+
+/****************************************************************************
+ *        Transformation from tree to binary representation of jsonb        *
+ ****************************************************************************/
+typedef struct CompressState
+{
+	char	*begin;
+	char	*ptr;
+
+	struct {
+		uint32	i;
+		uint32	*header;
+		JEntry	*array;
+		char	*begin;
+	} *levelstate, *lptr, *pptr;
+
+	uint32	maxlevel;
+	
+} CompressState;
+
+#define	curLevelState	state->lptr
+#define prevLevelState	state->pptr
+
+static void
+putJEntryString(CompressState *state, JsonbValue* value, uint32 level, uint32 i)
+{
+	curLevelState = state->levelstate + level;
+
+	if (i == 0)
+		curLevelState->array[0].entry = JENTRY_ISFIRST;
+	else
+		curLevelState->array[i].entry = 0;
+
+	switch(value->type)
+	{
+		case jbvNull:
+			curLevelState->array[i].entry |= JENTRY_ISNULL;
+
+			if (i>0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvString:
+			memcpy(state->ptr, value->string.val, value->string.len);
+			state->ptr += value->string.len;
+
+			if (i == 0)
+				curLevelState->array[i].entry |= value->string.len;
+			else
+				curLevelState->array[i].entry |= 
+					(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+					value->string.len;
+			break;
+		case jbvBool:
+			curLevelState->array[i].entry |= (value->boolean) ?
+				JENTRY_ISTRUE : JENTRY_ISFALSE;
+
+			if (i>0)
+				curLevelState->array[i].entry |=
+					curLevelState->array[i - 1].entry & JENTRY_POSMASK;
+			break;
+		case jbvNumeric:
+			{
+				int addlen = INTALIGN(state->ptr - state->begin) -
+								(state->ptr - state->begin);
+				int	numlen = VARSIZE_ANY(value->numeric); 
+
+				switch(addlen)
+				{
+					case 3:
+						*state->ptr = '\0'; state->ptr++;
+					case 2:
+						*state->ptr = '\0'; state->ptr++;
+					case 1:
+						*state->ptr = '\0'; state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->numeric, numlen);
+				state->ptr += numlen;
+
+				curLevelState->array[i].entry |= JENTRY_ISNUMERIC;
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + numlen;
+				else
+					curLevelState->array[i].entry |= 
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + numlen;
+				break;
+			}
+		case jbvBinary:
+			{
+				int addlen = INTALIGN(state->ptr - state->begin) -
+								(state->ptr - state->begin);
+
+				switch(addlen)
+				{
+					case 3:
+						*state->ptr = '\0'; state->ptr++;
+					case 2:
+						*state->ptr = '\0'; state->ptr++;
+					case 1:
+						*state->ptr = '\0'; state->ptr++;
+					case 0:
+					default:
+						break;
+				}
+
+				memcpy(state->ptr, value->binary.data, value->binary.len);
+				state->ptr += value->binary.len;
+
+				curLevelState->array[i].entry |= JENTRY_ISNEST;
+
+				if (i == 0)
+					curLevelState->array[i].entry |= addlen + value->binary.len;
+				else
+					curLevelState->array[i].entry |=
+						(curLevelState->array[i - 1].entry & JENTRY_POSMASK) +
+						addlen + value->binary.len;
+			}
+			break;
+		default:
+			elog(PANIC,"Unsupported JsonbValue type: %d", value->type);
+	}
+}
+
+static void
+compressCallback(void *arg, JsonbValue* value, uint32 flags, uint32 level)
+{
+	CompressState	*state = arg;
+
+	if (level == state->maxlevel) {
+		state->maxlevel *= 2;
+		state->levelstate = repalloc(state->levelstate,
+								 sizeof(*state->levelstate) * state->maxlevel);
+	}
+
+	curLevelState = state->levelstate + level;
+
+	if (flags & (WJB_BEGIN_ARRAY | WJB_BEGIN_OBJECT))
+	{
+		Assert(((flags & WJB_BEGIN_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_BEGIN_OBJECT) && value->type == jbvHash));
+
+		curLevelState->begin = state->ptr;
+
+		switch(INTALIGN(state->ptr - state->begin) -
+			   (state->ptr - state->begin))
+		{
+			case 3:
+				*state->ptr = '\0'; state->ptr++;
+			case 2:
+				*state->ptr = '\0'; state->ptr++;
+			case 1:
+				*state->ptr = '\0'; state->ptr++;
+			case 0:
+			default:
+				break;
+		}
+
+		curLevelState->header = (uint32*)state->ptr;
+		state->ptr += sizeof(*curLevelState->header);
+
+		curLevelState->array = (JEntry*)state->ptr;
+		curLevelState->i = 0;
+
+		if (value->type == jbvArray)
+		{
+			*curLevelState->header = value->array.nelems | JB_FLAG_ARRAY ;
+			state->ptr += sizeof(JEntry) * value->array.nelems;
+
+			if (value->array.scalar)
+			{
+				Assert(value->array.nelems == 1);
+				Assert(level == 0);
+				*curLevelState->header |= JB_FLAG_SCALAR;
+			}
+		}
+		else
+		{
+			*curLevelState->header = value->hash.npairs | JB_FLAG_OBJECT ;
+			state->ptr += sizeof(JEntry) * value->hash.npairs * 2;
+		}
+	}
+	else if (flags & WJB_ELEM)
+	{
+		putJEntryString(state, value, level, curLevelState->i);
+		curLevelState->i++;
+	}
+	else if (flags & WJB_KEY)
+	{
+		Assert(value->type == jbvString);
+
+		putJEntryString(state, value, level, curLevelState->i * 2);
+	}
+	else if (flags & WJB_VALUE)
+	{
+		putJEntryString(state, value, level, curLevelState->i * 2 + 1);
+		curLevelState->i++;
+	}
+	else if (flags & (WJB_END_ARRAY | WJB_END_OBJECT))
+	{
+		uint32	len, i;
+
+		Assert(((flags & WJB_END_ARRAY) && value->type == jbvArray) ||
+			   ((flags & WJB_END_OBJECT) && value->type == jbvHash));
+		if (level == 0)
+			return;
+
+		len = state->ptr - (char*)curLevelState->begin;
+
+		prevLevelState = curLevelState - 1;
+
+		if (*prevLevelState->header & JB_FLAG_ARRAY) {
+			i = prevLevelState->i;
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			if (i == 0)
+				prevLevelState->array[0].entry |= JENTRY_ISFIRST | len;
+			else
+				prevLevelState->array[i].entry |=
+					(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else if (*prevLevelState->header & JB_FLAG_OBJECT)
+		{
+			i = 2 * prevLevelState->i + 1; /* VALUE, not a KEY */
+
+			prevLevelState->array[i].entry = JENTRY_ISNEST;
+
+			prevLevelState->array[i].entry |=
+				(prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len;
+		}
+		else
+		{
+			elog(PANIC, "Wrong parent");
+		}
+
+		Assert(state->ptr - curLevelState->begin <= value->size);
+		prevLevelState->i++;
+	}
+	else
+	{
+		elog(PANIC, "Wrong flags");
+	}
+}
+
+uint32
+compressJsonb(JsonbValue *v, char *buffer) {
+	uint32			l = 0;
+	CompressState	state;
+
+	state.begin = state.ptr = buffer;
+	state.maxlevel = 8;
+	state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel);
+
+	walkUncompressedJsonb(v, compressCallback, &state);
+
+	l = state.ptr - buffer;
+	Assert(l <= v->size);
+
+	return l;
+}
+
+/****************************************************************************
+ *                  Iteration-like forming jsonb                            *
+ *       Note: it believes by default in already sorted keys in hash,       *
+ *     although with r == WJB_END_OBJECT and v == NULL  it will sort itself *
+ ****************************************************************************/
+static ToJsonbState*
+pushState(ToJsonbState **state)
+{
+	ToJsonbState	*ns = palloc(sizeof(*ns));
+
+	ns->next = *state;
+	return ns;
+}
+
+static void
+appendArray(ToJsonbState *state, JsonbValue *v)
+{
+	JsonbValue	*a = &state->v;
+
+	Assert(a->type == jbvArray);
+
+	if (a->array.nelems >= state->size)
+	{
+		state->size *= 2;
+		a->array.elems = repalloc(a->array.elems,
+								   sizeof(*a->array.elems) * state->size);
+	}
+
+	a->array.elems[a->array.nelems ++] = *v;
+
+	a->size += v->size;
+}
+
+static void
+appendKey(ToJsonbState *state, JsonbValue *v)
+{
+	JsonbValue	*h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	if (h->hash.npairs >= state->size)
+	{
+		state->size *= 2;
+		h->hash.pairs = repalloc(h->hash.pairs,
+									sizeof(*h->hash.pairs) * state->size);
+	}
+
+	h->hash.pairs[h->hash.npairs].key = *v;
+	h->hash.pairs[h->hash.npairs].order = h->hash.npairs;
+
+	h->size += v->size;
+}
+
+static void
+appendValue(ToJsonbState *state, JsonbValue *v)
+{
+
+	JsonbValue	*h = &state->v;
+
+	Assert(h->type == jbvHash);
+
+	h->hash.pairs[h->hash.npairs++].value = *v;
+
+	h->size += v->size;
+}
+
+
+JsonbValue*
+pushJsonbValue(ToJsonbState **state, int r /* WJB_* */, JsonbValue *v) {
+	JsonbValue	*h = NULL;
+
+	switch(r)
+	{
+		case WJB_BEGIN_ARRAY:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvArray;
+			(*state)->v.size = 3*sizeof(JEntry);
+			(*state)->v.array.nelems = 0;
+			(*state)->v.array.scalar = (v && v->array.scalar) ? true : false;
+			(*state)->size = (v && v->type == jbvArray && v->array.nelems > 0)
+								? v->array.nelems : 4;
+			(*state)->v.array.elems = palloc(sizeof(*(*state)->v.array.elems) *
+											 (*state)->size);
+			break;
+		case WJB_BEGIN_OBJECT:
+			*state = pushState(state);
+			h = &(*state)->v;
+			(*state)->v.type = jbvHash;
+			(*state)->v.size = 3*sizeof(JEntry);
+			(*state)->v.hash.npairs = 0;
+			(*state)->size = (v && v->type == jbvHash && v->hash.npairs > 0) ?
+									v->hash.npairs : 4;
+			(*state)->v.hash.pairs = palloc(sizeof(*(*state)->v.hash.pairs) *
+											(*state)->size);
+			break;
+		case WJB_ELEM:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric || 
+				   v->type == jbvBinary); 
+			appendArray(*state, v);
+			break;
+		case WJB_KEY:
+			Assert(v->type == jbvString);
+			appendKey(*state, v);
+			break;
+		case WJB_VALUE:
+			Assert(v->type == jbvNull || v->type == jbvString ||
+				   v->type == jbvBool || v->type == jbvNumeric || 
+				   v->type == jbvBinary); 
+			appendValue(*state, v);
+			break;
+		case WJB_END_OBJECT:
+			h = &(*state)->v;
+			if (v == NULL)
+				uniqueJsonbValue(h);
+		case WJB_END_ARRAY:
+			h = &(*state)->v;
+			*state = (*state)->next;
+			if (*state)
+			{
+				switch((*state)->v.type)
+				{
+					case jbvArray:
+						appendArray(*state, h);
+						break;
+					case jbvHash:
+						appendValue(*state, h);
+						break;
+					default:
+						elog(PANIC, "wrong parent type: %d", (*state)->v.type);
+				}
+			}
+			break;
+		default:
+			elog(PANIC, "wrong type: %08x", r);
+	}
+
+	return h;
+}
+
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 90fa447..20cf215 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -27,6 +27,7 @@
 #include "utils/builtins.h"
 #include "utils/hsearch.h"
 #include "utils/json.h"
+#include "utils/jsonb.h"
 #include "utils/jsonapi.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -59,6 +60,7 @@ static void alen_array_element_start(void *state, bool isnull);
 
 /* common worker for json_each* functions */
 static inline Datum each_worker(PG_FUNCTION_ARGS, bool as_text);
+static inline Datum each_worker_jsonb(PG_FUNCTION_ARGS, bool as_text);
 
 /* semantic action functions for json_each */
 static void each_object_field_start(void *state, char *fname, bool isnull);
@@ -218,12 +220,86 @@ typedef struct PopulateRecordsetState
  *
  * This SRF operates in value-per-call mode. It processes the
  * object during the first call, and the keys are simply stashed
- * in an array, whise size is expanded as necessary. This is probably
+ * in an array, whose size is expanded as necessary. This is probably
  * safe enough for a list of keys of a single object, since they are
  * limited in size to NAMEDATALEN and the number of keys is unlikely to
  * be so huge that it has major memory implications.
  */
 
+Datum
+jsonb_object_keys(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	OkeysState *state;
+	int			i;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		MemoryContext    oldcontext;
+		Jsonb           *jb = PG_GETARG_JSONB(0);
+		bool             skipNested = false;
+		JsonbIterator   *it;
+		JsonbValue	     v;
+		int              r = 0;
+
+	 
+		if (JB_ROOT_IS_SCALAR(jb))
+			elog(ERROR,"Cannot call jsonb_object_keys on a scalar");
+		else if (JB_ROOT_IS_ARRAY(jb))
+			elog(ERROR,"Cannot call jsonb_object_keys on an array");
+
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		state = palloc(sizeof(OkeysState));
+
+		state->result_size = JB_ROOT_COUNT(jb);
+		state->result_count = 0;
+		state->sent_count = 0;
+		state->result = palloc(state->result_size * sizeof(char *));
+
+		it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+		while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+		{
+			skipNested = true;
+			
+			if (r == WJB_KEY)
+			{
+				char * cstr;
+
+				cstr = palloc(v.string.len+1 * sizeof(char));
+				memcpy(cstr,v.string.val, v.string.len);
+				cstr[v.string.len] = '\0';
+				state->result[state->result_count++] = cstr;
+			}
+		}
+
+
+		MemoryContextSwitchTo(oldcontext);
+		funcctx->user_fctx = (void *) state;
+
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+	state = (OkeysState *) funcctx->user_fctx;
+
+	if (state->sent_count < state->result_count)
+	{
+		char	   *nxt = state->result[state->sent_count++];
+
+		SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
+	}
+
+	/* cleanup to reduce or eliminate memory leaks */
+	for (i = 0; i < state->result_count; i++)
+		pfree(state->result[i]);
+	pfree(state->result);
+	pfree(state);
+
+	SRF_RETURN_DONE(funcctx);
+}
+
 
 Datum
 json_object_keys(PG_FUNCTION_ARGS)
@@ -234,12 +310,26 @@ json_object_keys(PG_FUNCTION_ARGS)
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		text	   *json = PG_GETARG_TEXT_P(0);
-		JsonLexContext *lex = makeJsonLexContext(json, true);
+		text	   *json; //  = PG_GETARG_TEXT_P(0);
+		JsonLexContext *lex; //  = makeJsonLexContext(json, true);
 		JsonSemAction *sem;
 
 		MemoryContext oldcontext;
 
+		if (get_fn_expr_argtype(fcinfo->flinfo, 0) == JSONOID)
+		{
+			/* just get the text */
+			json = PG_GETARG_TEXT_P(0);
+		}
+		else
+		{
+			Jsonb      *jb = PG_GETARG_JSONB(0);
+			
+			json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+		}
+
+		lex = makeJsonLexContext(json, true);
+
 		funcctx = SRF_FIRSTCALL_INIT();
 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
@@ -336,9 +426,9 @@ okeys_scalar(void *state, char *token, JsonTokenType tokentype)
 }
 
 /*
- * json getter functions
+ * json and jsonb getter functions
  * these implement the -> ->> #> and #>> operators
- * and the json_extract_path*(json, text, ...) functions
+ * and the json{b?}_extract_path*(json, text, ...) functions
  */
 
 
@@ -359,6 +449,50 @@ json_object_field(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	char	   *key =  text_to_cstring(PG_GETARG_TEXT_P(1));
+	int         klen = strlen(key);
+	
+	JsonbIterator   *it;
+	JsonbValue       v;
+	int              r = 0;
+	bool             skipNested = false;
+    
+   if (JB_ROOT_IS_SCALAR(jb))
+       elog(ERROR,"Cannot call jsonb_object_field on a scalar");
+   else if (JB_ROOT_IS_ARRAY(jb))
+       elog(ERROR,"Cannot call jsonb_object_field on an array");
+
+   Assert(JB_ROOT_IS_OBJECT(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+		
+		// elog(NOTICE, "r = %d",r);
+		
+		if (r == WJB_KEY)
+		{
+			if (klen == v.string.len && strncmp(key, v.string.val, klen) == 0)
+			{
+				/*
+				 * The next thing the iterator fetches should be the
+				 * value, no matter what shape it is.
+				 */
+				r = JsonbIteratorGet(&it, &v, skipNested);
+				PG_RETURN_JSONB(JsonbValueToJsonb(&v));
+			}
+		}
+	}
+
+	PG_RETURN_NULL(); 
+}
+
+Datum
 json_object_field_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -375,6 +509,70 @@ json_object_field_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_object_field_text(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	char	   *key =  text_to_cstring(PG_GETARG_TEXT_P(1));
+	int         klen = strlen(key);
+	
+	JsonbIterator   *it;
+	JsonbValue       v;
+	int              r = 0;
+	bool             skipNested = false;
+    
+	if (JB_ROOT_IS_SCALAR(jb))
+		elog(ERROR,"Cannot call jsonb_object_field on a scalar");
+	else if (JB_ROOT_IS_ARRAY(jb))
+		elog(ERROR,"Cannot call jsonb_object_field on an array");
+	
+	Assert(JB_ROOT_IS_OBJECT(jb));
+	
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+	
+	while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+		
+		// elog(NOTICE, "r = %d",r);
+		
+		if (r == WJB_KEY)
+		{
+			if (klen == v.string.len && strncmp(key, v.string.val, klen) == 0)
+			{
+				text *result;
+				/*
+				 * The next thing the iterator fetches should be the
+				 * value, no matter what shape it is.
+				 */
+				r = JsonbIteratorGet(&it, &v, skipNested);
+				/* 
+				 * if it's a scalar string it needs to be de-escaped, 
+				 * otherwise just return the text 
+				 */
+				if (v.type == jbvString)
+				{
+					result = cstring_to_text_with_len(v.string.val, v.string.len);
+				}
+				else if (v.type == jbvNull)
+				{
+					PG_RETURN_NULL(); 
+				}
+				else
+				{
+					StringInfo jtext = makeStringInfo();
+					Jsonb *tjb = JsonbValueToJsonb(&v);
+					(void) JsonbToCString(jtext, VARDATA(tjb), -1);
+					result = cstring_to_text_with_len(jtext->data, jtext->len);
+				}
+				PG_RETURN_TEXT_P(result);
+			}
+		}
+	}
+
+	PG_RETURN_NULL(); 
+}
+
+Datum
 json_array_element(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -390,6 +588,40 @@ json_array_element(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	int			element = PG_GETARG_INT32(1);
+	JsonbIterator   *it;
+	JsonbValue       v;
+	int              r = 0;
+	bool             skipNested = false;
+	int              element_number = 0;
+    
+   if (JB_ROOT_IS_SCALAR(jb))
+       elog(ERROR,"Cannot call jsonb_array_element on a scalar");
+   else if (JB_ROOT_IS_OBJECT(jb))
+       elog(ERROR,"Cannot call jsonb_array_element on an object");
+
+   Assert(JB_ROOT_IS_ARRAY(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+		
+		if (r == WJB_ELEM)
+		{
+			if (element_number++ == element)
+				PG_RETURN_JSONB(JsonbValueToJsonb(&v));
+		}
+	}
+
+	PG_RETURN_NULL(); 
+}
+
+Datum
 json_array_element_text(PG_FUNCTION_ARGS)
 {
 	text	   *json = PG_GETARG_TEXT_P(0);
@@ -405,24 +637,97 @@ json_array_element_text(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_array_element_text(PG_FUNCTION_ARGS)
+{
+	Jsonb      *jb = PG_GETARG_JSONB(0);
+	int			element = PG_GETARG_INT32(1);
+	JsonbIterator   *it;
+	JsonbValue       v;
+	int              r = 0;
+	bool             skipNested = false;
+	int              element_number = 0;
+
+    
+   if (JB_ROOT_IS_SCALAR(jb))
+       elog(ERROR,"Cannot call jsonb_array_element_text on a scalar");
+   else if (JB_ROOT_IS_OBJECT(jb))
+       elog(ERROR,"Cannot call jsonb_array_element_text on an object");
+
+   Assert(JB_ROOT_IS_ARRAY(jb));
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+		
+		// elog(NOTICE, "r = %d",r);
+		
+		if (r == WJB_ELEM)
+		{
+			
+			if (element_number++ == element)
+			{
+				/* 
+				 * if it's a scalar string it needs to be de-escaped, 
+				 * otherwise just return the text 
+				 */
+				text *result;
+				if (v.type == jbvString)
+				{
+					result = cstring_to_text_with_len(v.string.val, v.string.len);
+				}
+				else if (v.type == jbvNull)
+				{
+					PG_RETURN_NULL(); 
+				}
+				else
+				{
+					StringInfo jtext = makeStringInfo();
+					Jsonb *tjb = JsonbValueToJsonb(&v);
+					(void) JsonbToCString(jtext, VARDATA(tjb), -1);
+					result = cstring_to_text_with_len(jtext->data, jtext->len);
+				}
+				PG_RETURN_TEXT_P(result);
+			}
+		}
+	}
+
+	PG_RETURN_NULL(); 
+}
+
+Datum
 json_extract_path(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, false);
 }
 
 Datum
+jsonb_extract_path(PG_FUNCTION_ARGS)
+{
+	return get_path_all(fcinfo, false);
+}
+
+Datum
 json_extract_path_text(PG_FUNCTION_ARGS)
 {
 	return get_path_all(fcinfo, true);
 }
 
+Datum
+jsonb_extract_path_text(PG_FUNCTION_ARGS)
+{
+	return get_path_all(fcinfo, true);
+}
+
 /*
  * common routine for extract_path functions
  */
 static inline Datum
 get_path_all(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	text	   *json;
 	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
 	text	   *result;
 	Datum	   *pathtext;
@@ -434,6 +739,19 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	long		ind;
 	char	   *endptr;
 
+	Assert(val_type == JSONOID || val_type == JSONBOID);
+	if (val_type == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(0);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(0);
+
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
+
 	if (array_contains_nulls(path))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -472,9 +790,17 @@ get_path_all(PG_FUNCTION_ARGS, bool as_text)
 	result = get_worker(json, NULL, -1, tpath, ipath, npath, as_text);
 
 	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
+	{
+		if (val_type == JSONOID || as_text)
+			PG_RETURN_TEXT_P(result);
+		else
+			PG_RETURN_JSONB(DirectFunctionCall1(jsonb_in, CStringGetDatum(text_to_cstring(result))));
+	}
 	else
+	{
+		/* null is null regardless */
 		PG_RETURN_NULL();
+	}
 }
 
 /*
@@ -654,7 +980,7 @@ get_object_field_end(void *state, char *fname, bool isnull)
 		/*
 		 * make a text object from the string from the prevously noted json
 		 * start up to the end of the previous token (the lexer is by now
-		 * ahead of us on whatevere came after what we're interested in).
+		 * ahead of us on whatever came after what we're interested in).
 		 */
 		int			len = _state->lex->prev_token_terminator - _state->result_start;
 
@@ -814,12 +1140,14 @@ get_scalar(void *state, char *token, JsonTokenType tokentype)
 Datum
 json_array_length(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *json;
 
 	AlenState  *state;
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 
+	json = PG_GETARG_TEXT_P(0);
+	lex = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(AlenState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -839,6 +1167,24 @@ json_array_length(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(state->count);
 }
 
+Datum
+jsonb_array_length(PG_FUNCTION_ARGS)
+{
+	Jsonb *jb = PG_GETARG_JSONB(0);
+
+	if (JB_ROOT_IS_SCALAR(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a scalar")));
+	else if (! JB_ROOT_IS_ARRAY(jb))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot get array length of a non-array")));
+
+	PG_RETURN_INT32(JB_ROOT_COUNT(jb));
+	
+}
+
 /*
  * These next two check ensure that the json is an array (since it can't be
  * a scalar or an object).
@@ -895,22 +1241,166 @@ json_each(PG_FUNCTION_ARGS)
 }
 
 Datum
+jsonb_each(PG_FUNCTION_ARGS)
+{
+	return each_worker_jsonb(fcinfo, false);
+}
+
+Datum
 json_each_text(PG_FUNCTION_ARGS)
 {
 	return each_worker(fcinfo, true);
 }
 
+Datum
+jsonb_each_text(PG_FUNCTION_ARGS)
+{
+	return each_worker_jsonb(fcinfo, true);
+}
+
+static inline Datum
+each_worker_jsonb(PG_FUNCTION_ARGS, bool as_text)
+{
+	Jsonb *jb = PG_GETARG_JSONB(0);
+	ReturnSetInfo *rsi;
+	Tuplestorestate *tuple_store;
+	TupleDesc	tupdesc;
+	TupleDesc	ret_tdesc;
+	MemoryContext old_cxt, tmp_cxt;
+	bool             skipNested = false;
+	JsonbIterator   *it;
+	JsonbValue	     v;
+	int              r = 0;
+
+	if (!JB_ROOT_IS_OBJECT(jb))
+		elog(ERROR,"cannot call %s on a non-object",
+			 as_text ? "jsonb_each_text" : "jsonb_each");
+
+	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0 ||
+		rsi->expectedDesc == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that "
+						"cannot accept a set")));
+
+
+	rsi->returnMode = SFRM_Materialize;
+
+	(void) get_call_result_type(fcinfo, NULL, &tupdesc);
+
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	ret_tdesc = CreateTupleDescCopy(tupdesc);
+	BlessTupleDesc(ret_tdesc);
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
+									"jsonb_each temporary cxt",
+									ALLOCSET_DEFAULT_MINSIZE,
+									ALLOCSET_DEFAULT_INITSIZE,
+									ALLOCSET_DEFAULT_MAXSIZE);
+
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+		
+		if (r == WJB_KEY)
+		{
+			text *key;
+			HeapTuple	tuple;
+			Datum		values[2];
+			bool		nulls[2] = {false, false};
+			
+			/* use the tmp context so we can clean up after each tuple is done */
+			old_cxt = MemoryContextSwitchTo(tmp_cxt);
+
+			key = cstring_to_text_with_len(v.string.val, v.string.len);
+
+			/*
+			 * The next thing the iterator fetches should be the
+			 * value, no matter what shape it is.
+			 */
+			r = JsonbIteratorGet(&it, &v, skipNested);
+			 
+			values[0] = PointerGetDatum(key);
+
+			if (as_text)
+			{
+				if (v.type == jbvNull)
+				{
+					/* a json null is an sql null in text mode */
+					nulls[1] = true;
+					values[1] = (Datum) NULL;
+				}
+				else 
+				{ 
+					text *sv;
+
+					if (v.type == jbvString)
+					{
+						/* in text mode scalar strings should be dequoted */
+						sv = cstring_to_text_with_len(v.string.val, v.string.len);
+					}
+					else
+					{
+						/* turn anything else into a json string */
+						StringInfo jtext = makeStringInfo();
+						Jsonb *jb = JsonbValueToJsonb(&v);
+						(void) JsonbToCString(jtext, VARDATA(jb), 2 * v.size);
+						sv = cstring_to_text_with_len(jtext->data, jtext->len);
+					}
+
+					values[1] = PointerGetDatum(sv);
+				}
+			}
+			else
+			{
+				/* not in text mode, just return the Jsonb */
+				Jsonb *val = JsonbValueToJsonb(&v);
+				values[1] = PointerGetDatum(val);
+			}
+
+			tuple = heap_form_tuple(ret_tdesc, values, nulls);
+
+			tuplestore_puttuple(tuple_store, tuple);
+
+			/* clean up and switch back */
+			MemoryContextSwitchTo(old_cxt);
+			MemoryContextReset(tmp_cxt);
+		}
+	}
+
+	rsi->setResult = tuple_store;
+	rsi->setDesc = ret_tdesc;
+
+	PG_RETURN_NULL();	
+}
+
+
 static inline Datum
 each_worker(PG_FUNCTION_ARGS, bool as_text)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	JsonLexContext *lex = makeJsonLexContext(json, true);
+	text	   *json;
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	TupleDesc	tupdesc;
 	EachState  *state;
 
+	json = PG_GETARG_TEXT_P(0);
+
+	lex = makeJsonLexContext(json, true);
 	state = palloc0(sizeof(EachState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -1067,19 +1557,113 @@ each_scalar(void *state, char *token, JsonTokenType tokentype)
  *
  * a lot of this processing is similar to the json_each* functions
  */
+
 Datum
-json_array_elements(PG_FUNCTION_ARGS)
+jsonb_array_elements(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	Jsonb *jb = PG_GETARG_JSONB(0);
+	ReturnSetInfo *rsi;
+	Tuplestorestate *tuple_store;
+	TupleDesc	tupdesc;
+	TupleDesc	ret_tdesc;
+	MemoryContext old_cxt, tmp_cxt;
+	bool             skipNested = false;
+	JsonbIterator   *it;
+	JsonbValue	     v;
+	int              r = 0;
 
-	/* elements doesn't need any escaped strings, so use false here */
-	JsonLexContext *lex = makeJsonLexContext(json, false);
+	if (JB_ROOT_IS_SCALAR(jb)) 
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot extract elements from a scalar")));
+	else if (! JB_ROOT_IS_ARRAY(jb)) 
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("cannot extract elements from an object")));
+
+	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0 ||
+		rsi->expectedDesc == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that "
+						"cannot accept a set")));
+
+
+	rsi->returnMode = SFRM_Materialize;
+
+	/* it's a simple type, so don't use get_call_result_type() */
+	tupdesc = rsi->expectedDesc;
+
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	ret_tdesc = CreateTupleDescCopy(tupdesc);
+	BlessTupleDesc(ret_tdesc);
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
+									"jsonb_each temporary cxt",
+									ALLOCSET_DEFAULT_MINSIZE,
+									ALLOCSET_DEFAULT_INITSIZE,
+									ALLOCSET_DEFAULT_MAXSIZE);
+
+
+	it = JsonbIteratorInit(VARDATA_ANY(jb));
+
+	while((r = JsonbIteratorGet(&it, &v, skipNested)) != 0)
+	{
+		skipNested = true;
+		
+		if (r == WJB_ELEM)
+		{
+			HeapTuple	tuple;
+			Datum		values[1];
+			bool		nulls[1] = {false};
+			Jsonb      *val;
+			
+			/* use the tmp context so we can clean up after each tuple is done */
+			old_cxt = MemoryContextSwitchTo(tmp_cxt);
+
+			val = JsonbValueToJsonb(&v);
+			values[0] = PointerGetDatum(val);
+
+			tuple = heap_form_tuple(ret_tdesc, values, nulls);
+
+			tuplestore_puttuple(tuple_store, tuple);
+
+			/* clean up and switch back */
+			MemoryContextSwitchTo(old_cxt);
+			MemoryContextReset(tmp_cxt);
+		}
+	}
+
+	rsi->setResult = tuple_store;
+	rsi->setDesc = ret_tdesc;
+
+	PG_RETURN_NULL();	
+}
+
+Datum
+json_array_elements(PG_FUNCTION_ARGS)
+{
+	text	   *json;
+	JsonLexContext *lex;
 	JsonSemAction *sem;
 	ReturnSetInfo *rsi;
 	MemoryContext old_cxt;
 	TupleDesc	tupdesc;
 	ElementsState *state;
 
+	json = PG_GETARG_TEXT_P(0);
+
+	/* elements doesn't need any escaped strings, so use false here */
+	lex  = makeJsonLexContext(json, false);
 	state = palloc0(sizeof(ElementsState));
 	sem = palloc0(sizeof(JsonSemAction));
 
@@ -1214,9 +1798,16 @@ elements_scalar(void *state, char *token, JsonTokenType tokentype)
  * field in the record is then looked up by name.
  */
 Datum
+jsonb_populate_record(PG_FUNCTION_ARGS)
+{
+	return json_populate_record(fcinfo);
+}
+
+Datum
 json_populate_record(PG_FUNCTION_ARGS)
 {
 	Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	Oid         jtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
 	text	   *json;
 	bool		use_json_as_text;
 	HTAB	   *json_hash;
@@ -1234,6 +1825,8 @@ json_populate_record(PG_FUNCTION_ARGS)
 	char		fname[NAMEDATALEN];
 	JsonHashEntry *hashentry;
 
+	Assert(jtype == JSONOID || jtype == JSONBOID);
+
 	use_json_as_text = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
 
 	if (!type_is_rowtype(argtype))
@@ -1268,7 +1861,17 @@ json_populate_record(PG_FUNCTION_ARGS)
 		tupTypmod = HeapTupleHeaderGetTypMod(rec);
 	}
 
-	json = PG_GETARG_TEXT_P(1);
+	if (jtype == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(1);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(1);
+		
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
 
 	json_hash = get_json_object_as_hash(json, "json_populate_record", use_json_as_text);
 
@@ -1559,9 +2162,17 @@ hash_scalar(void *state, char *token, JsonTokenType tokentype)
  * per object in the array.
  */
 Datum
+jsonb_populate_recordset(PG_FUNCTION_ARGS)
+{
+	return json_populate_recordset(fcinfo);
+}
+
+
+Datum
 json_populate_recordset(PG_FUNCTION_ARGS)
 {
 	Oid			argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
+	Oid         jtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
 	text	   *json;
 	bool		use_json_as_text;
 	ReturnSetInfo *rsi;
@@ -1621,7 +2232,17 @@ json_populate_recordset(PG_FUNCTION_ARGS)
 	if (PG_ARGISNULL(1))
 		PG_RETURN_NULL();
 
-	json = PG_GETARG_TEXT_P(1);
+	if (jtype == JSONOID)
+	{
+		/* just get the text */
+		json = PG_GETARG_TEXT_P(1);
+	}
+	else
+	{
+		Jsonb      *jb = PG_GETARG_JSONB(1);
+		
+		json = cstring_to_text(JsonbToCString(NULL, (JB_ISEMPTY(jb)) ? NULL : VARDATA(jb), VARSIZE(jb)));
+	}
 
 	if (PG_ARGISNULL(0))
 		rec = NULL;
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 6aa4890..143a451 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1753,6 +1753,18 @@ DATA(insert OID = 3966 (  "#>"	   PGNSP PGUID b f f 114 1009 114 0 0 json_extrac
 DESCR("get value from json with path elements");
 DATA(insert OID = 3967 (  "#>>"    PGNSP PGUID b f f 114 1009 25 0 0 json_extract_path_text_op - - ));
 DESCR("get value from json as text with path elements");
+DATA(insert OID = 3211 (  "->"	   PGNSP PGUID b f f 3802 25 3802 0 0 jsonb_object_field - - ));
+DESCR("get jsonb object field");
+DATA(insert OID = 3204 (  "->>"    PGNSP PGUID b f f 3802 25 25 0 0 jsonb_object_field_text - - ));
+DESCR("get jsonb object field as text");
+DATA(insert OID = 3212 (  "->"	   PGNSP PGUID b f f 3802 23 3802 0 0 jsonb_array_element - - ));
+DESCR("get jsonb array element");
+DATA(insert OID = 3205 (  "->>"    PGNSP PGUID b f f 3802 23 25 0 0 jsonb_array_element_text - - ));
+DESCR("get jsonb array element as text");
+DATA(insert OID = 3213 (  "#>"	   PGNSP PGUID b f f 3802 1009 3802 0 0 jsonb_extract_path_op - - ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3206 (  "#>>"    PGNSP PGUID b f f 3802 1009 25 0 0 jsonb_extract_path_text_op - - ));
+DESCR("get value from jsonb as text with path elements");
 
 
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ab05c46..bf776d1 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4457,6 +4457,44 @@ DESCR("I/O");
 DATA(insert OID = 3774 (  regdictionarysend PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3769" _null_ _null_ _null_ _null_ regdictionarysend _null_ _null_ _null_ ));
 DESCR("I/O");
 
+/* jsonb */
+DATA(insert OID =  3806 (  jsonb_in			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2275" _null_ _null_ _null_ _null_ jsonb_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3805 (  jsonb_recv		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 3802 "2281" _null_ _null_ _null_ _null_ jsonb_recv _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3804 (  jsonb_out		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3802" _null_ _null_ _null_ _null_ jsonb_out _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID =  3803 (  jsonb_send		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3802" _null_ _null_ _null_ _null_	jsonb_send _null_ _null_ _null_ ));
+DESCR("I/O");
+
+DATA(insert OID = 3969 (  jsonb_object_field			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field _null_ _null_ _null_ ));
+DATA(insert OID = 3179 (  jsonb_object_field_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field_text _null_ _null_ _null_ ));
+DATA(insert OID = 3180 (  jsonb_array_element		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element _null_ _null_ _null_ ));
+DATA(insert OID = 3195 (  jsonb_array_element_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element_text _null_ _null_ _null_ ));
+DATA(insert OID = 3196 (  jsonb_extract_path			PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 3802 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DESCR("get value from jsonb with path elements");
+DATA(insert OID = 3199 (  jsonb_extract_path_op		PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path _null_ _null_ _null_ ));
+DATA(insert OID = 3200 (  jsonb_extract_path_text	PGNSP PGUID 12 1 0 25 0 f f f f t f i 2 0 25 "3802 1009" "{3802,1009}" "{i,v}" "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DESCR("get value from jsonb as text with path elements");
+DATA(insert OID = 3197 (  jsonb_extract_path_text_op PGNSP PGUID 12 1 0 0 0	f f f f t f i 2 0 25 "3802 1009" _null_ _null_ "{from_json,path_elems}" _null_ jsonb_extract_path_text _null_ _null_ _null_ ));
+DATA(insert OID = 3198 (  jsonb_array_elements		PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 3802 "3802" "{3802,3802}" "{i,o}" "{from_json,value}" _null_ jsonb_array_elements _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3207 (  jsonb_array_length			PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "3802" _null_ _null_ _null_ _null_ jsonb_array_length _null_ _null_ _null_ ));
+DESCR("length of jsonb array");
+DATA(insert OID = 3201 (  jsonb_object_keys			PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_object_keys _null_ _null_ _null_ ));
+DESCR("get jsonb object keys");
+DATA(insert OID = 3208 (  jsonb_each				   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,3802}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3202 (  jsonb_each_text		   PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2249 "3802" "{3802,25,25}" "{i,o,o}" "{from_json,key,value}" _null_ jsonb_each_text _null_ _null_ _null_ ));
+DESCR("key value pairs of a jsonb object");
+DATA(insert OID = 3209 (  jsonb_populate_record	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_record _null_ _null_ _null_ ));
+DESCR("get record fields from a jsonb object");
+DATA(insert OID = 3203 (  jsonb_populate_recordset  PGNSP PGUID 12 1 100 0 0 f f f f f t s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_recordset _null_ _null_ _null_ ));
+DESCR("get set of records with fields from a jsonb array of objects");
+DATA(insert OID = 3210 (  jsonb_typeof              PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_typeof _null_ _null_ _null_ ));
+DESCR("get the type of a jsonb value");
+
+
 /* txid */
 DATA(insert OID = 2939 (  txid_snapshot_in			PGNSP PGUID 12 1  0 0 0 f f f f t f i 1 0 2970 "2275" _null_ _null_ _null_ _null_ txid_snapshot_in _null_ _null_ _null_ ));
 DESCR("I/O");
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 3fc20c6..7fb8999 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -600,6 +600,12 @@ DATA(insert OID = 3645 ( _tsquery		PGNSP PGUID -1 f b A f t \054 0 3615 0 array_
 DATA(insert OID = 3735 ( _regconfig		PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
 
+/* jsonb */
+DATA(insert OID = 3802 ( jsonb			PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DESCR("Binary JSON");
+#define JSONBOID 3802
+DATA(insert OID = 3807 ( _jsonb			PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+
 DATA(insert OID = 2970 ( txid_snapshot	PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
 DESCR("txid snapshot");
 DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 9982e59..3610fc8 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -293,6 +293,15 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
 		PG_RETURN_DATUM(_result); \
 	} while (0)
 
+#define SRF_RETURN_NEXT_NULL(_funcctx) \
+	do { \
+		ReturnSetInfo	   *rsi; \
+		(_funcctx)->call_cntr++; \
+		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+		rsi->isDone = ExprMultipleResult; \
+		PG_RETURN_NULL(); \
+	} while (0)
+
 #define  SRF_RETURN_DONE(_funcctx) \
 	do { \
 		ReturnSetInfo	   *rsi; \
diff --git a/src/include/utils/json.h b/src/include/utils/json.h
index 25bfafb..c2fc7ee 100644
--- a/src/include/utils/json.h
+++ b/src/include/utils/json.h
@@ -50,4 +50,18 @@ extern Datum json_array_elements(PG_FUNCTION_ARGS);
 extern Datum json_populate_record(PG_FUNCTION_ARGS);
 extern Datum json_populate_recordset(PG_FUNCTION_ARGS);
 
+extern Datum jsonb_object_field(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_field_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_element_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path(PG_FUNCTION_ARGS);
+extern Datum jsonb_extract_path_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_object_keys(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_length(PG_FUNCTION_ARGS);
+extern Datum jsonb_each(PG_FUNCTION_ARGS);
+extern Datum jsonb_each_text(PG_FUNCTION_ARGS);
+extern Datum jsonb_array_elements(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_record(PG_FUNCTION_ARGS);
+extern Datum jsonb_populate_recordset(PG_FUNCTION_ARGS);
+
 #endif   /* JSON_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
new file mode 100644
index 0000000..15e2927
--- /dev/null
+++ b/src/include/utils/jsonb.h
@@ -0,0 +1,230 @@
+/*-------------------------------------------------------------------------
+ *
+ * jsonb.h
+ *    Declarations for JSONB data type support.
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ *
+ * NOTE. JSONB type is designed to be binary compatible with hstore.
+ *
+ * src/include/utils/jsonb.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef __JSONB_H__
+#define __JSONB_H__
+
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "utils/array.h"
+#include "utils/numeric.h"
+
+/*
+ * JEntry: there is one of these for each key _and_ value in an jsonb
+ *
+ * the position offset points to the _end_ so that we can get the length
+ * by subtraction from the previous entry.	the ISFIRST flag lets us tell
+ * whether there is a previous entry.
+ */
+typedef struct
+{
+	uint32		entry;
+} JEntry;
+
+#define JENTRY_ISFIRST		0x80000000
+#define JENTRY_ISSTRING 	(0x00000000) /* keep binary compatibility */
+#define JENTRY_ISNUMERIC	(0x10000000)
+#define JENTRY_ISNEST		(0x20000000)
+#define JENTRY_ISNULL		(0x40000000) /* keep binary compatibility */
+#define JENTRY_ISBOOL		(0x10000000 | 0x20000000)
+#define JENTRY_ISFALSE		JENTRY_ISBOOL
+#define JENTRY_ISTRUE		(0x10000000 | 0x20000000 | 0x40000000)
+
+/* JENTRY_ISOBJECT, JENTRY_ISARRAY and JENTRY_ISCALAR is only used in send/recv */
+#define JENTRY_ISOBJECT		(0x20000000)
+#define JENTRY_ISARRAY		(0x20000000 | 0x40000000)
+#define JENTRY_ISCALAR		(0x10000000 | 0x40000000)
+
+#define JENTRY_POSMASK 	0x0FFFFFFF
+#define JENTRY_TYPEMASK	(~(JENTRY_POSMASK | JENTRY_ISFIRST))
+
+/* note possible multiple evaluations, also access to prior array element */
+#define JBE_ISFIRST(he_) 		(((he_).entry & JENTRY_ISFIRST) != 0)
+#define JBE_ISSTRING(he_)		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
+#define JBE_ISNUMERIC(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
+#define JBE_ISNEST(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNEST)
+#define JBE_ISNULL(he_) 		(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNULL)
+#define JBE_ISBOOL(he_) 		(((he_).entry & JENTRY_TYPEMASK & JENTRY_ISBOOL) == JENTRY_ISBOOL)
+#define JBE_ISBOOL_TRUE(he_) 	(((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISTRUE)
+#define JBE_ISBOOL_FALSE(he_) 	(JBE_ISBOOL(he_) && !JBE_ISBOOL_TRUE(he_))
+
+#define JBE_ENDPOS(he_) ((he_).entry & JENTRY_POSMASK)
+#define JBE_OFF(he_) (JBE_ISFIRST(he_) ? 0 : JBE_ENDPOS((&(he_))[-1]))
+#define JBE_LEN(he_) (JBE_ISFIRST(he_)	\
+					  ? JBE_ENDPOS(he_) \
+					  : JBE_ENDPOS(he_) - JBE_ENDPOS((&(he_))[-1]))
+
+/*
+ * determined by the size of "endpos" (ie JENTRY_POSMASK)
+ */
+#define JSONB_MAX_STRING_LEN 		JENTRY_POSMASK
+
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	/* header of hash or array jsonb type */
+	/* array of JEntry follows */
+} Jsonb;
+
+/*
+ * it's not possible to get more than 2^28 items into an jsonb.
+ */
+#define JB_FLAG_UNUSED 			0x80000000
+#define JB_FLAG_ARRAY			0x40000000
+#define JB_FLAG_OBJECT			0x20000000
+#define JB_FLAG_SCALAR			0x10000000
+
+#define JB_COUNT_MASK			0x0FFFFFFF
+
+#define JB_ISEMPTY(jbp_)		(VARSIZE(jbp_) <= VARHDRSZ)
+#define JB_ROOT_COUNT(jbp_) 	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_COUNT_MASK))
+#define JB_ROOT_IS_OBJECT(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_OBJECT))
+#define JB_ROOT_IS_ARRAY(jbp_) 	(JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_ARRAY))
+#define JB_ROOT_IS_SCALAR(jbp_) (JB_ISEMPTY(jbp_) ? 0 : ( *(uint32*)VARDATA(jbp_) & JB_FLAG_SCALAR))
+
+#define DatumGetJsonb(d) 	((Jsonb*) PG_DETOAST_DATUM(d))
+#define JsonbGetDatum(p)	PointerGetDatum(p)
+
+#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x)
+
+typedef struct JsonbPair JsonbPair;
+typedef struct JsonbValue JsonbValue;
+
+struct JsonbValue {
+	enum {
+		jbvNull,
+		jbvString,
+		jbvNumeric,
+		jbvBool,
+		jbvArray,
+		jbvHash,
+		jbvBinary  /* binary form of jbvArray/jbvHash */
+	} type;
+
+	uint32		size; /* estimation size of node (including subnodes) */
+
+	union {
+		Numeric			numeric;
+		bool			boolean;
+		struct {
+			uint32		len;
+			char 		*val; /* could be not null-terminated */
+		} string;
+
+		struct {
+			int			nelems;
+			JsonbValue	*elems;
+			bool		scalar; /* scalar actually shares representation with array */
+		} array;
+
+		struct {
+			int			npairs;
+			JsonbPair 	*pairs;
+		} hash;
+
+		struct {
+			uint32		len;
+			char		*data;
+		} binary;
+	};
+
+}; 
+
+struct JsonbPair {
+	JsonbValue	key;
+	JsonbValue	value;
+	uint32		order; /* to keep order of pairs with equal key */ 
+}; 
+
+/*
+ * jsonb support functios
+ */
+
+#define WJB_KEY         	(0x001)
+#define WJB_VALUE       	(0x002)
+#define WJB_ELEM       		(0x004)
+#define WJB_BEGIN_ARRAY 	(0x008)
+#define WJB_END_ARRAY   	(0x010)
+#define WJB_BEGIN_OBJECT    (0x020)
+#define WJB_END_OBJECT      (0x040)
+
+typedef void (*walk_jsonb_cb)(void* /*arg*/, JsonbValue* /* value */, 
+											uint32 /* flags */, uint32 /* level */);
+extern void walkUncompressedJsonb(JsonbValue *v, walk_jsonb_cb cb, void *cb_arg);
+
+extern int compareJsonbStringValue(const void *a, const void *b, void *arg);
+extern int compareJsonbPair(const void *a, const void *b, void *arg);
+
+extern int compareJsonbBinaryValue(char *a, char *b);
+extern int compareJsonbValue(JsonbValue *a, JsonbValue *b);
+
+extern JsonbValue* findUncompressedJsonbValueByValue(char *buffer, uint32 flags, 
+												uint32 *lowbound, JsonbValue* key);
+extern JsonbValue* findUncompressedJsonbValue(char *buffer, uint32 flags, 
+												uint32 *lowbound, char *key, uint32 keylen);
+
+extern JsonbValue* getJsonbValue(char *buffer, uint32 flags, int32 i);
+
+typedef struct ToJsonbState
+{
+	JsonbValue             v;
+	uint32                  size;
+	struct ToJsonbState    *next;
+} ToJsonbState;
+
+extern JsonbValue* pushJsonbValue(ToJsonbState **state, int r /* WJB_* */, JsonbValue *v);
+
+extern void uniqueJsonbValue(JsonbValue *v);
+
+extern uint32 compressJsonb(JsonbValue *v, char *buffer);
+
+typedef struct JsonbIterator
+{
+	uint32					type;
+	uint32					nelems;
+	JEntry					*array;
+	bool					isScalar;
+	char					*data;
+	char					*buffer; /* unparsed buffer */
+
+	int						i;
+
+	/*
+	 * enum members should be freely OR'ed with JB_FLAG_ARRAY/JB_FLAG_JSONB 
+	 * with possiblity of decoding. See optimization in JsonbIteratorGet()
+	 */
+	enum {
+		jbi_start 	= 0x00,
+		jbi_key		= 0x01,
+		jbi_value	= 0x02,
+		jbi_elem	= 0x04
+	} state;
+
+	struct JsonbIterator	*next;
+} JsonbIterator;
+
+extern 	JsonbIterator*	JsonbIteratorInit(char *buffer);
+extern	int /* WJB_* */	JsonbIteratorGet(JsonbIterator **it, JsonbValue *v, bool skipNested);
+
+extern Datum jsonb_in(PG_FUNCTION_ARGS);
+extern Datum jsonb_out(PG_FUNCTION_ARGS);
+extern Datum jsonb_recv(PG_FUNCTION_ARGS);
+extern Datum jsonb_send(PG_FUNCTION_ARGS);
+
+extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
+
+extern char *JsonbToCString(StringInfo out, char *in, int estimated_len);
+extern Jsonb *JsonbValueToJsonb(JsonbValue *v);
+
+#endif   /* __JSONB_H__ */
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
new file mode 100644
index 0000000..aa00506
--- /dev/null
+++ b/src/test/regress/expected/jsonb.out
@@ -0,0 +1,845 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ octet_length 
+--------------
+            5
+(1 row)
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+      array_to_json       
+--------------------------
+ [{"a": 1},{"b": [2, 3]}]
+(1 row)
+
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_object_field on a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  Cannot call jsonb_object_field on an array
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_array_element on a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  Cannot call jsonb_array_element on an object
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  Cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ correct_in_utf8 
+-----------------
+              10
+(1 row)
+
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+   correct_in_utf8    
+----------------------
+ the Copyright © sign
+(1 row)
+
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
new file mode 100644
index 0000000..9fc7402
--- /dev/null
+++ b/src/test/regress/expected/jsonb_1.out
@@ -0,0 +1,845 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+ jsonb 
+-------
+ ""
+(1 row)
+
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT $$''$$::jsonb;
+               ^
+DETAIL:  Token "'" is invalid.
+CONTEXT:  JSON data, line 1: '...
+SELECT '"abc"'::jsonb;			-- OK
+ jsonb 
+-------
+ "abc"
+(1 row)
+
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc'::jsonb;
+               ^
+DETAIL:  Token ""abc" is invalid.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"abc
+               ^
+DETAIL:  Character with value 0x0a must be escaped.
+CONTEXT:  JSON data, line 1: "abc
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+  jsonb   
+----------
+ "\n\"\\"
+(1 row)
+
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\v"'::jsonb;
+               ^
+DETAIL:  Escape sequence "\v" is invalid.
+CONTEXT:  JSON data, line 1: "\v...
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u"
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u00"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u00"
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '"\u000g"'::jsonb;
+               ^
+DETAIL:  "\u" must be followed by four hexadecimal digits.
+CONTEXT:  JSON data, line 1: "\u000g...
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+   jsonb   
+-----------
+ "\\u0000"
+(1 row)
+
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT octet_length('"\uaBcD"'::jsonb::text);
+                            ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: ...
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+ jsonb 
+-------
+ 1
+(1 row)
+
+SELECT '0'::jsonb;				-- OK
+ jsonb 
+-------
+ 0
+(1 row)
+
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '01'::jsonb;
+               ^
+DETAIL:  Token "01" is invalid.
+CONTEXT:  JSON data, line 1: 01
+SELECT '0.1'::jsonb;				-- OK
+ jsonb 
+-------
+ 0.1
+(1 row)
+
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+        jsonb        
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '1e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1.3e100'::jsonb;			-- OK
+                                                 jsonb                                                 
+-------------------------------------------------------------------------------------------------------
+ 13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT '1f2'::jsonb;				-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1f2'::jsonb;
+               ^
+DETAIL:  Token "1f2" is invalid.
+CONTEXT:  JSON data, line 1: 1f2
+SELECT '0.x1'::jsonb;			-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '0.x1'::jsonb;
+               ^
+DETAIL:  Token "0.x1" is invalid.
+CONTEXT:  JSON data, line 1: 0.x1
+SELECT '1.3ex100'::jsonb;		-- ERROR
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '1.3ex100'::jsonb;
+               ^
+DETAIL:  Token "1.3ex100" is invalid.
+CONTEXT:  JSON data, line 1: 1.3ex100
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+ jsonb 
+-------
+ []
+(1 row)
+
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+                                                                                                  jsonb                                                                                                   
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+(1 row)
+
+SELECT '[1,2]'::jsonb;			-- OK
+ jsonb  
+--------
+ [1, 2]
+(1 row)
+
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2,]'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found "]".
+CONTEXT:  JSON data, line 1: [1,2,]
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,2'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,2
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '[1,[2]'::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: [1,[2]
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+ jsonb 
+-------
+ {}
+(1 row)
+
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found "}".
+CONTEXT:  JSON data, line 1: {"abc"}
+SELECT '{"abc":1}'::jsonb;		-- OK
+   jsonb    
+------------
+ {"abc": 1}
+(1 row)
+
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{1:"abc"}'::jsonb;
+               ^
+DETAIL:  Expected string or "}", but found "1".
+CONTEXT:  JSON data, line 1: {1...
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc",1}'::jsonb;
+               ^
+DETAIL:  Expected ":", but found ",".
+CONTEXT:  JSON data, line 1: {"abc",...
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"=1}'::jsonb;
+               ^
+DETAIL:  Token "=" is invalid.
+CONTEXT:  JSON data, line 1: {"abc"=...
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc"::1}'::jsonb;
+               ^
+DETAIL:  Expected JSON value, but found ":".
+CONTEXT:  JSON data, line 1: {"abc"::...
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+                               jsonb                                
+--------------------------------------------------------------------
+ {"abc": 1, "def": 2, "ghi": [3, 4], "hij": {"klm": 5, "nop": [6]}}
+(1 row)
+
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1:2}'::jsonb;
+               ^
+DETAIL:  Expected "," or "}", but found ":".
+CONTEXT:  JSON data, line 1: {"abc":1:...
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '{"abc":1,3}'::jsonb;
+               ^
+DETAIL:  Expected string, but found "3".
+CONTEXT:  JSON data, line 1: {"abc":1,3...
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'false'::jsonb;			-- OK
+ jsonb 
+-------
+ false
+(1 row)
+
+SELECT 'null'::jsonb;			-- OK
+ jsonb 
+-------
+ null
+(1 row)
+
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+ jsonb 
+-------
+ true
+(1 row)
+
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found "false".
+CONTEXT:  JSON data, line 1: true false
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'true, false'::jsonb;
+               ^
+DETAIL:  Expected end of input, but found ",".
+CONTEXT:  JSON data, line 1: true,...
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'truf'::jsonb;
+               ^
+DETAIL:  Token "truf" is invalid.
+CONTEXT:  JSON data, line 1: truf
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT 'trues'::jsonb;
+               ^
+DETAIL:  Token "trues" is invalid.
+CONTEXT:  JSON data, line 1: trues
+SELECT ''::jsonb;				-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT ''::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT '    '::jsonb;			-- ERROR, no value
+ERROR:  invalid input syntax for type json
+LINE 1: SELECT '    '::jsonb;
+               ^
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1:     
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+      array_to_json       
+--------------------------
+ [{"a": 1},{"b": [2, 3]}]
+(1 row)
+
+-- jsonb extraction functions
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_object_field on a scalar
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  Cannot call jsonb_object_field on an array
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ "val2"
+(1 row)
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+ ?column? 
+----------
+ val2
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_array_element on a scalar
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ "two"
+(1 row)
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+ERROR:  Cannot call jsonb_array_element on an object
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+ ?column? 
+----------
+ two
+(1 row)
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+ERROR:  Cannot call jsonb_object_keys on a scalar
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+ERROR:  Cannot call jsonb_object_keys on an array
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+ jsonb_object_keys 
+-------------------
+ field1
+ field2
+ field3
+(3 rows)
+
+-- nulls
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+ expect_true 
+-------------
+ t
+(1 row)
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+ expect_false 
+--------------
+ f
+(1 row)
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- array length
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ jsonb_array_length 
+--------------------
+                  5
+(1 row)
+
+SELECT jsonb_array_length('[]');
+ jsonb_array_length 
+--------------------
+                  0
+(1 row)
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot get array length of a non-array
+SELECT jsonb_array_length('4');
+ERROR:  cannot get array length of a scalar
+-- each
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+     jsonb_each     
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,null)
+(3 rows)
+
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | null
+ f5  | 99
+ f6  | "stringy"
+(5 rows)
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+  jsonb_each_text   
+--------------------
+ (f1,"[1, 2, 3]")
+ (f2,"{""f3"": 1}")
+ (f4,)
+ (f5,null)
+(4 rows)
+
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+ key |   value   
+-----+-----------
+ f1  | [1, 2, 3]
+ f2  | {"f3": 1}
+ f4  | 
+ f5  | 99
+ f6  | stringy
+(5 rows)
+
+-- extract_path, extract_path_as_text
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path 
+--------------------
+ "stringy"
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path 
+--------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path 
+--------------------
+ "f3"
+(1 row)
+
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path 
+--------------------
+ 1
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ jsonb_extract_path_text 
+-------------------------
+ stringy
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ jsonb_extract_path_text 
+-------------------------
+ {"f3": 1}
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ jsonb_extract_path_text 
+-------------------------
+ f3
+(1 row)
+
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ jsonb_extract_path_text 
+-------------------------
+ 1
+(1 row)
+
+-- extract_path nulls
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+ expect_false 
+--------------
+ f
+(1 row)
+
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+ expect_true 
+-------------
+ t
+(1 row)
+
+-- extract_path operators
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+ ?column?  
+-----------
+ "stringy"
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+ ?column? 
+----------
+ "f3"
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+ ?column? 
+----------
+ stringy
+(1 row)
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+ ?column?  
+-----------
+ {"f3": 1}
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+ ?column? 
+----------
+ f3
+(1 row)
+
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+ ?column? 
+----------
+ 1
+(1 row)
+
+-- array_elements
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+    jsonb_array_elements    
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+           value            
+----------------------------
+ 1
+ true
+ [1, [2, 3]]
+ null
+ {"f1": 1, "f2": [7, 8, 9]}
+ false
+(6 rows)
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b | c 
+--------+---+---
+ blurfl |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b | c 
+-------------------+---+---
+ [100, 200, false] |   | 
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+         a         | b |            c             
+-------------------+---+--------------------------
+ [100, 200, false] | 3 | Mon Dec 31 15:30:56 2012
+(1 row)
+
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, false]"
+-- populate_recordset
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+        a        | b  |            c             
+-----------------+----+--------------------------
+ [100, 200, 300] | 99 | 
+ {"z": true}     |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+ERROR:  invalid input syntax for type timestamp: "[100, 200, 300]"
+-- using the default use_json_as_text argument
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b |            c             
+--------+---+--------------------------
+ blurfl |   | 
+        | 3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+   a    | b  |            c             
+--------+----+--------------------------
+ blurfl | 99 | 
+ def    |  3 | Fri Jan 20 10:42:53 2012
+(2 rows)
+
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+ERROR:  cannot call json_populate_recordset on a nested object
+-- handling of unicode surrogate pairs
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc3...
+                                   ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode high surrogate must not follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ud83dX" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "\ude04X" }' -> 'a';
+                     ^
+DETAIL:  Unicode low surrogate must follow a high surrogate.
+CONTEXT:  JSON data, line 1: { "a":...
+--handling of simple unicode escapes
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+ERROR:  invalid input syntax for type json
+LINE 1: select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a'...
+                     ^
+DETAIL:  Unicode escape values cannot be used for code point values above 007F when the server encoding is not UTF8.
+CONTEXT:  JSON data, line 1: { "a":...
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+ correct_everywhere 
+--------------------
+ dollar $ character
+(1 row)
+
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+   not_unescaped    
+--------------------
+ null \u0000 escape
+(1 row)
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);
+         value          | jsonb_typeof 
+------------------------+--------------
+ 123.4                  | number
+ -1                     | number
+ "foo"                  | string
+ true                   | boolean
+ false                  | boolean
+ null                   | null
+ [1, 2, 3]              | array
+ []                     | array
+ {"x": "foo", "y": 123} | object
+ {}                     | object
+                        | 
+(11 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 5758b07..51238be 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -98,8 +98,7 @@ test: event_trigger
 # ----------
 # Another group of parallel tests
 # ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json indirect_toast
-
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast
 # ----------
 # Another group of parallel tests
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 78348f5..e414ec1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -121,6 +121,7 @@ test: xmlmap
 test: functional_deps
 test: advisory_lock
 test: json
+test: jsonb
 test: indirect_toast
 test: plancache
 test: limit
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
new file mode 100644
index 0000000..38959a8
--- /dev/null
+++ b/src/test/regress/sql/jsonb.sql
@@ -0,0 +1,265 @@
+-- Strings.
+SELECT '""'::jsonb;				-- OK.
+SELECT $$''$$::jsonb;			-- ERROR, single quotes are not allowed
+SELECT '"abc"'::jsonb;			-- OK
+SELECT '"abc'::jsonb;			-- ERROR, quotes not closed
+SELECT '"abc
+def"'::jsonb;					-- ERROR, unescaped newline in string constant
+SELECT '"\n\"\\"'::jsonb;		-- OK, legal escapes
+SELECT '"\v"'::jsonb;			-- ERROR, not a valid JSON escape
+SELECT '"\u"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u00"'::jsonb;			-- ERROR, incomplete escape
+SELECT '"\u000g"'::jsonb;		-- ERROR, g is not a hex digit
+SELECT '"\u0000"'::jsonb;		-- OK, legal escape
+-- use octet_length here so we don't get an odd unicode char in the
+-- output
+SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower case both OK
+
+-- Numbers.
+SELECT '1'::jsonb;				-- OK
+SELECT '0'::jsonb;				-- OK
+SELECT '01'::jsonb;				-- ERROR, not valid according to JSON spec
+SELECT '0.1'::jsonb;				-- OK
+SELECT '9223372036854775808'::jsonb;	-- OK, even though it's too large for int8
+SELECT '1e100'::jsonb;			-- OK
+SELECT '1.3e100'::jsonb;			-- OK
+SELECT '1f2'::jsonb;				-- ERROR
+SELECT '0.x1'::jsonb;			-- ERROR
+SELECT '1.3ex100'::jsonb;		-- ERROR
+
+-- Arrays.
+SELECT '[]'::jsonb;				-- OK
+SELECT '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'::jsonb;  -- OK
+SELECT '[1,2]'::jsonb;			-- OK
+SELECT '[1,2,]'::jsonb;			-- ERROR, trailing comma
+SELECT '[1,2'::jsonb;			-- ERROR, no closing bracket
+SELECT '[1,[2]'::jsonb;			-- ERROR, no closing bracket
+
+-- Objects.
+SELECT '{}'::jsonb;				-- OK
+SELECT '{"abc"}'::jsonb;			-- ERROR, no value
+SELECT '{"abc":1}'::jsonb;		-- OK
+SELECT '{1:"abc"}'::jsonb;		-- ERROR, keys must be strings
+SELECT '{"abc",1}'::jsonb;		-- ERROR, wrong separator
+SELECT '{"abc"=1}'::jsonb;		-- ERROR, totally wrong separator
+SELECT '{"abc"::1}'::jsonb;		-- ERROR, another wrong separator
+SELECT '{"abc":1,"def":2,"ghi":[3,4],"hij":{"klm":5,"nop":[6]}}'::jsonb; -- OK
+SELECT '{"abc":1:2}'::jsonb;		-- ERROR, colon in wrong spot
+SELECT '{"abc":1,3}'::jsonb;		-- ERROR, no value
+
+-- Miscellaneous stuff.
+SELECT 'true'::jsonb;			-- OK
+SELECT 'false'::jsonb;			-- OK
+SELECT 'null'::jsonb;			-- OK
+SELECT ' true '::jsonb;			-- OK, even with extra whitespace
+SELECT 'true false'::jsonb;		-- ERROR, too many values
+SELECT 'true, false'::jsonb;		-- ERROR, too many values
+SELECT 'truf'::jsonb;			-- ERROR, not a keyword
+SELECT 'trues'::jsonb;			-- ERROR, not a keyword
+SELECT ''::jsonb;				-- ERROR, no value
+SELECT '    '::jsonb;			-- ERROR, no value
+
+-- make sure jsonb is passed throught json generators without being escaped
+select array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
+
+
+-- jsonb extraction functions
+
+CREATE TEMP TABLE test_jsonb (
+       json_type text,
+       test_json jsonb
+);
+
+INSERT INTO test_jsonb VALUES
+('scalar','"a scalar"'),
+('array','["zero", "one","two",null,"four","five"]'),
+('object','{"field1":"val1","field2":"val2","field3":null}');
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 'x'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>'field2'
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT test_json -> 2
+FROM test_jsonb
+WHERE json_type = 'object';
+
+SELECT test_json->>2
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'scalar';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'array';
+
+SELECT jsonb_object_keys(test_json)
+FROM test_jsonb
+WHERE json_type = 'object';
+
+-- nulls
+
+select (test_json->'field3') is null as expect_false
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->>'field3') is null as expect_true
+from test_jsonb
+where json_type = 'object';
+
+select (test_json->3) is null as expect_false
+from test_jsonb
+where json_type = 'array';
+
+select (test_json->>3) is null as expect_true
+from test_jsonb
+where json_type = 'array';
+
+
+-- array length
+
+SELECT jsonb_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+
+SELECT jsonb_array_length('[]');
+
+SELECT jsonb_array_length('{"f1":1,"f2":[5,6]}');
+
+SELECT jsonb_array_length('4');
+
+-- each
+
+select jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null}');
+select * from jsonb_each('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+select jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
+select * from jsonb_each_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
+
+-- extract_path, extract_path_as_text
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select jsonb_extract_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+
+-- extract_path nulls
+
+select jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+select jsonb_extract_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+select jsonb_extract_path_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+
+-- extract_path operators
+
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>array['f2','1'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f4','f6'];
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','0'];
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>array['f2','1'];
+
+-- same using array literals
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>'{f2,1}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f4,f6}';
+select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,0}';
+select '{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}'::jsonb#>>'{f2,1}';
+
+-- array_elements
+
+select jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]');
+select * from jsonb_array_elements('[1,true,[1,[2,3]],null,{"f1":1,"f2":[7,8,9]},false]') q;
+
+
+-- populate_record
+create type jbpop as (a text, b int, c timestamp);
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}') q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}') q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":"blurfl","x":43.2}', true) q;
+
+select * from jsonb_populate_record(null::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"a":[100,200,false],"x":43.2}', true) q;
+select * from jsonb_populate_record(row('x',3,'2012-12-31 15:30:56')::jbpop,'{"c":[100,200,false],"x":43.2}', true) q;
+
+-- populate_recordset
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',false) q;
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]',true) q;
+
+-- using the default use_json_as_text argument
+
+select * from jsonb_populate_recordset(null::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":"blurfl","x":43.2},{"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+select * from jsonb_populate_recordset(row('def',99,null)::jbpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+
+
+-- handling of unicode surrogate pairs
+
+select octet_length((jsonb '{ "a":  "\ud83d\ude04\ud83d\udc36" }' -> 'a')::text)  as correct_in_utf8;
+select jsonb '{ "a":  "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a row
+select jsonb '{ "a":  "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong order
+select jsonb '{ "a":  "\ud83dX" }' -> 'a'; -- orphan high surrogate
+select jsonb '{ "a":  "\ude04X" }' -> 'a'; -- orphan low surrogate
+
+--handling of simple unicode escapes
+
+select jsonb '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
+select jsonb '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
+select jsonb '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+
+--jsonb_typeof() function
+select value, jsonb_typeof(value)
+  from (values (jsonb '123.4'),
+               (jsonb '-1'),
+               (jsonb '"foo"'),
+               (jsonb 'true'),
+               (jsonb 'false'),
+               (jsonb 'null'),
+               (jsonb '[1, 2, 3]'),
+               (jsonb '[]'),
+               (jsonb '{"x":"foo", "y":123}'),
+               (jsonb '{}'),
+               (NULL::jsonb))
+      as data(value);