[PATCH] ltree, lquery, and ltxtquery binary protocol support

Started by Nino Florisover 6 years ago12 messages
#1Nino Floris
mail@ninofloris.com
1 attachment(s)

Hi,

Attached is a patch to support send/recv on ltree, lquery and ltxtquery.
I'm a contributor to the Npgsql .NET PostgreSQL driver and we'll be
the first to have official support once those ltree changes have been
released.
You can find the driver support work here, the tests verify a
roundtrip for each of the types is succesful.
https://github.com/NinoFloris/npgsql/tree/label-tree-support

Any feedback would be highly appreciated.

Greetings,
Nino Floris

Attachments:

0001-Implements-ltree-lquery-and-ltxtquery-binary-protoco.patchapplication/octet-stream; name=0001-Implements-ltree-lquery-and-ltxtquery-binary-protoco.patchDownload
From 8f43d2e60bf2a91bc326a50ceb0675ad7e476557 Mon Sep 17 00:00:00 2001
From: Nino Floris <mail@ninofloris.com>
Date: Sun, 18 Aug 2019 16:30:30 +0200
Subject: [PATCH] Implements ltree, lquery, and ltxtquery binary protocol
 support

---
 contrib/ltree/Makefile       |    2 +-
 contrib/ltree/crc32.c        |    2 +-
 contrib/ltree/crc32.h        |    2 +-
 contrib/ltree/ltree--1.2.sql |  907 +++++++++++++++++++++++++
 contrib/ltree/ltree.control  |    2 +-
 contrib/ltree/ltree_io.c     | 1245 +++++++++++++++++++---------------
 contrib/ltree/ltxtquery_io.c |   75 +-
 7 files changed, 1680 insertions(+), 555 deletions(-)
 create mode 100644 contrib/ltree/ltree--1.2.sql

diff --git a/contrib/ltree/Makefile b/contrib/ltree/Makefile
index 416c8da312..2a17593212 100644
--- a/contrib/ltree/Makefile
+++ b/contrib/ltree/Makefile
@@ -6,7 +6,7 @@ OBJS = 	ltree_io.o ltree_op.o lquery_op.o _ltree_op.o crc32.o \
 PG_CPPFLAGS = -DLOWER_NODE
 
 EXTENSION = ltree
-DATA = ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
+DATA = ltree--1.2.sql ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
 PGFILEDESC = "ltree - hierarchical label data type"
 
 HEADERS = ltree.h
diff --git a/contrib/ltree/crc32.c b/contrib/ltree/crc32.c
index 447e4b2960..62939463a0 100644
--- a/contrib/ltree/crc32.c
+++ b/contrib/ltree/crc32.c
@@ -20,7 +20,7 @@
 #include "crc32.h"
 
 unsigned int
-ltree_crc32_sz(char *buf, int size)
+ltree_crc32_sz(const char *buf, int size)
 {
 	pg_crc32	crc;
 	char	   *p = buf;
diff --git a/contrib/ltree/crc32.h b/contrib/ltree/crc32.h
index 269d05d0c1..958812214d 100644
--- a/contrib/ltree/crc32.h
+++ b/contrib/ltree/crc32.h
@@ -4,7 +4,7 @@
 /* contrib/ltree/crc32.h */
 
 /* Returns crc32 of data block */
-extern unsigned int ltree_crc32_sz(char *buf, int size);
+extern unsigned int ltree_crc32_sz(const char *buf, int size);
 
 /* Returns crc32 of null-terminated string */
 #define crc32(buf) ltree_crc32_sz((buf),strlen(buf))
diff --git a/contrib/ltree/ltree--1.2.sql b/contrib/ltree/ltree--1.2.sql
new file mode 100644
index 0000000000..ab762597e4
--- /dev/null
+++ b/contrib/ltree/ltree--1.2.sql
@@ -0,0 +1,907 @@
+/* contrib/ltree/ltree--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION ltree" to load this file. \quit
+
+CREATE FUNCTION ltree_in(cstring)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_out(ltree)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_recv(internal)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_send(ltree)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE ltree (
+        INTERNALLENGTH = -1,
+        INPUT = ltree_in,
+        OUTPUT = ltree_out,
+        RECEIVE = ltree_recv,
+        SEND = ltree_send,
+        STORAGE = extended
+);
+
+--Compare function for ltree
+CREATE FUNCTION ltree_cmp(ltree,ltree)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_lt(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_le(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_eq(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_ge(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_gt(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_ne(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+
+CREATE OPERATOR < (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_lt,
+        COMMUTATOR = '>',
+	NEGATOR = '>=',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <= (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_le,
+        COMMUTATOR = '>=',
+	NEGATOR = '>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR >= (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_ge,
+        COMMUTATOR = '<=',
+	NEGATOR = '<',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR > (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_gt,
+        COMMUTATOR = '<',
+	NEGATOR = '<=',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR = (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_eq,
+        COMMUTATOR = '=',
+	NEGATOR = '<>',
+        RESTRICT = eqsel,
+	JOIN = eqjoinsel,
+        SORT1 = '<',
+	SORT2 = '<'
+);
+
+CREATE OPERATOR <> (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_ne,
+        COMMUTATOR = '<>',
+	NEGATOR = '=',
+        RESTRICT = neqsel,
+	JOIN = neqjoinsel
+);
+
+--util functions
+
+CREATE FUNCTION subltree(ltree,int4,int4)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subpath(ltree,int4,int4)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subpath(ltree,int4)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION index(ltree,ltree)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'ltree_index'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION index(ltree,ltree,int4)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'ltree_index'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION nlevel(ltree)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree2text(ltree)
+RETURNS text
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION text2ltree(text)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(_ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME','_lca'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_isparent(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_risparent(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_addltree(ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_addtext(ltree,text)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_textadd(text,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltreeparentsel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR @> (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_isparent,
+        COMMUTATOR = '<@',
+        RESTRICT = ltreeparentsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@> (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_isparent,
+        COMMUTATOR = '^<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_risparent,
+        COMMUTATOR = '@>',
+        RESTRICT = ltreeparentsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^<@ (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_risparent,
+        COMMUTATOR = '^@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR || (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_addltree
+);
+
+CREATE OPERATOR || (
+        LEFTARG = ltree,
+	RIGHTARG = text,
+	PROCEDURE = ltree_addtext
+);
+
+CREATE OPERATOR || (
+        LEFTARG = text,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_textadd
+);
+
+
+-- B-tree support
+
+CREATE OPERATOR CLASS ltree_ops
+    DEFAULT FOR TYPE ltree USING btree AS
+        OPERATOR        1       < ,
+        OPERATOR        2       <= ,
+        OPERATOR        3       = ,
+        OPERATOR        4       >= ,
+        OPERATOR        5       > ,
+        FUNCTION        1       ltree_cmp(ltree, ltree);
+
+
+--lquery type
+CREATE FUNCTION lquery_in(cstring)
+RETURNS lquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lquery_out(lquery)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lquery_recv(internal)
+RETURNS lquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lquery_send(lquery)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE lquery (
+	INTERNALLENGTH = -1,
+	INPUT = lquery_in,
+	OUTPUT = lquery_out,
+    RECEIVE = lquery_recv,
+    SEND = lquery_send,
+	STORAGE = extended
+);
+
+CREATE FUNCTION ltq_regex(ltree,lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltq_rregex(lquery,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ~ (
+        LEFTARG = ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = ltq_regex,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+        LEFTARG = lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltq_rregex,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--not-indexed
+CREATE OPERATOR ^~ (
+        LEFTARG = ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = ltq_regex,
+	COMMUTATOR = '^~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^~ (
+        LEFTARG = lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltq_rregex,
+	COMMUTATOR = '^~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION lt_q_regex(ltree,_lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lt_q_rregex(_lquery,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ? (
+        LEFTARG = ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = lt_q_regex,
+	COMMUTATOR = '?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ? (
+        LEFTARG = _lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = lt_q_rregex,
+	COMMUTATOR = '?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--not-indexed
+CREATE OPERATOR ^? (
+        LEFTARG = ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = lt_q_regex,
+	COMMUTATOR = '^?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^? (
+        LEFTARG = _lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = lt_q_rregex,
+	COMMUTATOR = '^?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION ltxtq_in(cstring)
+RETURNS ltxtquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_out(ltxtquery)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_recv(internal)
+RETURNS ltxtquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_send(ltxtquery)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE ltxtquery (
+	INTERNALLENGTH = -1,
+	INPUT = ltxtq_in,
+	OUTPUT = ltxtq_out,
+	RECEIVE = ltxtq_recv,
+	SEND = ltxtq_send,
+	STORAGE = extended
+);
+
+-- operations WITH ltxtquery
+
+CREATE FUNCTION ltxtq_exec(ltree, ltxtquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_rexec(ltxtquery, ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR @ (
+        LEFTARG = ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = ltxtq_exec,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltxtq_rexec,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--not-indexed
+CREATE OPERATOR ^@ (
+        LEFTARG = ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = ltxtq_exec,
+	COMMUTATOR = '^@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltxtq_rexec,
+	COMMUTATOR = '^@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--GiST support for ltree
+CREATE FUNCTION ltree_gist_in(cstring)
+RETURNS ltree_gist
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_gist_out(ltree_gist)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE ltree_gist (
+	internallength = -1,
+	input = ltree_gist_in,
+	output = ltree_gist_out,
+	storage = plain
+);
+
+
+CREATE FUNCTION ltree_consistent(internal,ltree,int2,oid,internal)
+RETURNS bool as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_compress(internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_decompress(internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_penalty(internal,internal,internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_picksplit(internal, internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_union(internal, internal)
+RETURNS ltree_gist as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_same(ltree_gist, ltree_gist, internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gist_ltree_ops
+    DEFAULT FOR TYPE ltree USING gist AS
+	OPERATOR	1	< ,
+	OPERATOR	2	<= ,
+	OPERATOR	3	= ,
+	OPERATOR	4	>= ,
+	OPERATOR	5	> ,
+	OPERATOR	10	@> ,
+	OPERATOR	11	<@ ,
+	OPERATOR	12	~ (ltree, lquery) ,
+	OPERATOR	13	~ (lquery, ltree) ,
+	OPERATOR	14	@ (ltree, ltxtquery) ,
+	OPERATOR	15	@ (ltxtquery, ltree) ,
+	OPERATOR	16	? (ltree, _lquery) ,
+	OPERATOR	17	? (_lquery, ltree) ,
+	FUNCTION	1	ltree_consistent (internal, ltree, int2, oid, internal),
+	FUNCTION	2	ltree_union (internal, internal),
+	FUNCTION	3	ltree_compress (internal),
+	FUNCTION	4	ltree_decompress (internal),
+	FUNCTION	5	ltree_penalty (internal, internal, internal),
+	FUNCTION	6	ltree_picksplit (internal, internal),
+	FUNCTION	7	ltree_same (ltree_gist, ltree_gist, internal),
+	STORAGE		ltree_gist;
+
+
+-- arrays of ltree
+
+CREATE FUNCTION _ltree_isparent(_ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_r_isparent(ltree,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_risparent(_ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_r_risparent(ltree,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltq_regex(_ltree,lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltq_rregex(lquery,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _lt_q_regex(_ltree,_lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _lt_q_rregex(_lquery,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltxtq_exec(_ltree, ltxtquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltxtq_rexec(ltxtquery, _ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR @> (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_isparent,
+        COMMUTATOR = '<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_isparent,
+        COMMUTATOR = '@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_risparent,
+        COMMUTATOR = '@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @> (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_risparent,
+        COMMUTATOR = '<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+        LEFTARG = _ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = _ltq_regex,
+        COMMUTATOR = '~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+        LEFTARG = lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltq_rregex,
+        COMMUTATOR = '~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ? (
+        LEFTARG = _ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = _lt_q_regex,
+        COMMUTATOR = '?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ? (
+        LEFTARG = _lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _lt_q_rregex,
+        COMMUTATOR = '?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = _ltxtq_exec,
+        COMMUTATOR = '@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltxtq_rexec,
+        COMMUTATOR = '@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+
+--not indexed
+CREATE OPERATOR ^@> (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_isparent,
+        COMMUTATOR = '^<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^<@ (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_isparent,
+        COMMUTATOR = '^@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^<@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_risparent,
+        COMMUTATOR = '^@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@> (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_risparent,
+        COMMUTATOR = '^<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^~ (
+        LEFTARG = _ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = _ltq_regex,
+        COMMUTATOR = '^~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^~ (
+        LEFTARG = lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltq_rregex,
+        COMMUTATOR = '^~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^? (
+        LEFTARG = _ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = _lt_q_regex,
+        COMMUTATOR = '^?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^? (
+        LEFTARG = _lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _lt_q_rregex,
+        COMMUTATOR = '^?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = _ltxtq_exec,
+        COMMUTATOR = '^@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltxtq_rexec,
+        COMMUTATOR = '^@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--extractors
+CREATE FUNCTION _ltree_extract_isparent(_ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?@> (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_extract_isparent
+);
+
+CREATE FUNCTION _ltree_extract_risparent(_ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?<@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_extract_risparent
+);
+
+CREATE FUNCTION _ltq_extract_regex(_ltree,lquery)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?~ (
+        LEFTARG = _ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = _ltq_extract_regex
+);
+
+CREATE FUNCTION _ltxtq_extract_exec(_ltree,ltxtquery)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = _ltxtq_extract_exec
+);
+
+--GiST support for ltree[]
+CREATE FUNCTION _ltree_consistent(internal,_ltree,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_union(internal, internal)
+RETURNS ltree_gist
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_same(ltree_gist, ltree_gist, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gist__ltree_ops
+    DEFAULT FOR TYPE _ltree USING gist AS
+	OPERATOR	10	<@ (_ltree, ltree),
+	OPERATOR	11	@> (ltree, _ltree),
+	OPERATOR	12	~ (_ltree, lquery),
+	OPERATOR	13	~ (lquery, _ltree),
+	OPERATOR	14	@ (_ltree, ltxtquery),
+	OPERATOR	15	@ (ltxtquery, _ltree),
+	OPERATOR	16	? (_ltree, _lquery),
+	OPERATOR	17	? (_lquery, _ltree),
+	FUNCTION	1	_ltree_consistent (internal, _ltree, int2, oid, internal),
+	FUNCTION	2	_ltree_union (internal, internal),
+	FUNCTION	3	_ltree_compress (internal),
+	FUNCTION	4	ltree_decompress (internal),
+	FUNCTION	5	_ltree_penalty (internal, internal, internal),
+	FUNCTION	6	_ltree_picksplit (internal, internal),
+	FUNCTION	7	_ltree_same (ltree_gist, ltree_gist, internal),
+	STORAGE		ltree_gist;
diff --git a/contrib/ltree/ltree.control b/contrib/ltree/ltree.control
index 03c3fb1ab5..61c8cdf40a 100644
--- a/contrib/ltree/ltree.control
+++ b/contrib/ltree/ltree.control
@@ -1,5 +1,5 @@
 # ltree extension
 comment = 'data type for hierarchical tree-like structures'
-default_version = '1.1'
+default_version = '1.2'
 module_pathname = '$libdir/ltree'
 relocatable = true
diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c
index f54f037443..2ecfbdd23a 100644
--- a/contrib/ltree/ltree_io.c
+++ b/contrib/ltree/ltree_io.c
@@ -4,28 +4,23 @@
  * contrib/ltree/ltree_io.c
  */
 #include "postgres.h"
+#include "libpq/pqformat.h"
 
 #include <ctype.h>
 
 #include "ltree.h"
 #include "utils/memutils.h"
+#include "utils/builtins.h"
 #include "crc32.h"
 
-PG_FUNCTION_INFO_V1(ltree_in);
-PG_FUNCTION_INFO_V1(ltree_out);
-PG_FUNCTION_INFO_V1(lquery_in);
-PG_FUNCTION_INFO_V1(lquery_out);
-
-
 #define UNCHAR ereport(ERROR, \
 					   (errcode(ERRCODE_SYNTAX_ERROR), \
 						errmsg("syntax error at position %d", \
 						pos)));
 
-
 typedef struct
 {
-	char	   *start;
+	const char	   *start;
 	int			len;			/* length in bytes */
 	int			flag;
 	int			wlen;			/* length in characters */
@@ -34,147 +29,225 @@ typedef struct
 #define LTPRS_WAITNAME	0
 #define LTPRS_WAITDELIM 1
 
+/*
+ * expects a null terminated string
+ * returns an ltree
+ */
+static ltree*
+parse_ltree(const char* buf)
+{
+    const char	   *ptr;
+    nodeitem   *list,
+            *lptr;
+    int			num = 0,
+            totallen = 0;
+    int			state = LTPRS_WAITNAME;
+    ltree	   *result;
+    ltree_level *curlevel;
+    int			charlen;
+    int			pos = 0;
+
+    ptr = buf;
+    while (*ptr)
+    {
+        charlen = pg_mblen(ptr);
+        if (charlen == 1 && t_iseq(ptr, '.'))
+            num++;
+        ptr += charlen;
+    }
+
+    if (num + 1 > MaxAllocSize / sizeof(nodeitem))
+        ereport(ERROR,
+                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                        errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
+                               num + 1, (int) (MaxAllocSize / sizeof(nodeitem)))));
+    list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
+    ptr = buf;
+    while (*ptr)
+    {
+        charlen = pg_mblen(ptr);
+
+        if (state == LTPRS_WAITNAME)
+        {
+            if (ISALNUM(ptr))
+            {
+                lptr->start = ptr;
+                lptr->wlen = 0;
+                state = LTPRS_WAITDELIM;
+            }
+            else
+                UNCHAR;
+        }
+        else if (state == LTPRS_WAITDELIM)
+        {
+            if (charlen == 1 && t_iseq(ptr, '.'))
+            {
+                lptr->len = ptr - lptr->start;
+                if (lptr->wlen > 255)
+                    ereport(ERROR,
+                            (errcode(ERRCODE_NAME_TOO_LONG),
+                                    errmsg("name of level is too long"),
+                                    errdetail("Name length is %d, must "
+                                              "be < 256, in position %d.",
+                                              lptr->wlen, pos)));
+
+                totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
+                lptr++;
+                state = LTPRS_WAITNAME;
+            }
+            else if (!ISALNUM(ptr))
+                UNCHAR;
+        }
+        else
+            /* internal error */
+            elog(ERROR, "internal error in parser");
+
+        ptr += charlen;
+        lptr->wlen++;
+        pos++;
+    }
+
+    if (state == LTPRS_WAITDELIM)
+    {
+        lptr->len = ptr - lptr->start;
+        if (lptr->wlen > 255)
+            ereport(ERROR,
+                    (errcode(ERRCODE_NAME_TOO_LONG),
+                            errmsg("name of level is too long"),
+                            errdetail("Name length is %d, must "
+                                      "be < 256, in position %d.",
+                                      lptr->wlen, pos)));
+
+        totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
+        lptr++;
+    }
+    else if (!(state == LTPRS_WAITNAME && lptr == list))
+        ereport(ERROR,
+                (errcode(ERRCODE_SYNTAX_ERROR),
+                        errmsg("syntax error"),
+                        errdetail("Unexpected end of line.")));
+
+    result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
+    SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
+    result->numlevel = lptr - list;
+    curlevel = LTREE_FIRST(result);
+    lptr = list;
+    while (lptr - list < result->numlevel)
+    {
+        curlevel->len = (uint16) lptr->len;
+        memcpy(curlevel->name, lptr->start, lptr->len);
+        curlevel = LEVEL_NEXT(curlevel);
+        lptr++;
+    }
+
+    pfree(list);
+    return result;
+}
+
+/*
+ * expects an ltree
+ * returns a null terminated string
+ */
+static char*
+deparse_ltree(const ltree* in)
+{
+    char	   *buf,
+            *ptr;
+    int			i;
+    ltree_level *curlevel;
+
+    ptr = buf = (char *) palloc(VARSIZE(in));
+    curlevel = LTREE_FIRST(in);
+    for (i = 0; i < in->numlevel; i++)
+    {
+    if (i != 0)
+    {
+    *ptr = '.';
+    ptr++;
+    }
+    memcpy(ptr, curlevel->name, curlevel->len);
+    ptr += curlevel->len;
+    curlevel = LEVEL_NEXT(curlevel);
+    }
+
+    *ptr = '\0';
+    return buf;
+}
+
+PG_FUNCTION_INFO_V1(ltree_in);
 Datum
 ltree_in(PG_FUNCTION_ARGS)
 {
 	char	   *buf = (char *) PG_GETARG_POINTER(0);
-	char	   *ptr;
-	nodeitem   *list,
-			   *lptr;
-	int			num = 0,
-				totallen = 0;
-	int			state = LTPRS_WAITNAME;
-	ltree	   *result;
-	ltree_level *curlevel;
-	int			charlen;
-	int			pos = 0;
-
-	ptr = buf;
-	while (*ptr)
-	{
-		charlen = pg_mblen(ptr);
-		if (charlen == 1 && t_iseq(ptr, '.'))
-			num++;
-		ptr += charlen;
-	}
-
-	if (num + 1 > MaxAllocSize / sizeof(nodeitem))
-		ereport(ERROR,
-				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-				 errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
-						num + 1, (int) (MaxAllocSize / sizeof(nodeitem)))));
-	list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
-	ptr = buf;
-	while (*ptr)
-	{
-		charlen = pg_mblen(ptr);
-
-		if (state == LTPRS_WAITNAME)
-		{
-			if (ISALNUM(ptr))
-			{
-				lptr->start = ptr;
-				lptr->wlen = 0;
-				state = LTPRS_WAITDELIM;
-			}
-			else
-				UNCHAR;
-		}
-		else if (state == LTPRS_WAITDELIM)
-		{
-			if (charlen == 1 && t_iseq(ptr, '.'))
-			{
-				lptr->len = ptr - lptr->start;
-				if (lptr->wlen > 255)
-					ereport(ERROR,
-							(errcode(ERRCODE_NAME_TOO_LONG),
-							 errmsg("name of level is too long"),
-							 errdetail("Name length is %d, must "
-									   "be < 256, in position %d.",
-									   lptr->wlen, pos)));
-
-				totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
-				lptr++;
-				state = LTPRS_WAITNAME;
-			}
-			else if (!ISALNUM(ptr))
-				UNCHAR;
-		}
-		else
-			/* internal error */
-			elog(ERROR, "internal error in parser");
-
-		ptr += charlen;
-		lptr->wlen++;
-		pos++;
-	}
-
-	if (state == LTPRS_WAITDELIM)
-	{
-		lptr->len = ptr - lptr->start;
-		if (lptr->wlen > 255)
-			ereport(ERROR,
-					(errcode(ERRCODE_NAME_TOO_LONG),
-					 errmsg("name of level is too long"),
-					 errdetail("Name length is %d, must "
-							   "be < 256, in position %d.",
-							   lptr->wlen, pos)));
-
-		totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
-		lptr++;
-	}
-	else if (!(state == LTPRS_WAITNAME && lptr == list))
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("syntax error"),
-				 errdetail("Unexpected end of line.")));
-
-	result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
-	SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
-	result->numlevel = lptr - list;
-	curlevel = LTREE_FIRST(result);
-	lptr = list;
-	while (lptr - list < result->numlevel)
-	{
-		curlevel->len = (uint16) lptr->len;
-		memcpy(curlevel->name, lptr->start, lptr->len);
-		curlevel = LEVEL_NEXT(curlevel);
-		lptr++;
-	}
-
-	pfree(list);
+	ltree      *result = parse_ltree(buf);
 	PG_RETURN_POINTER(result);
 }
 
+PG_FUNCTION_INFO_V1(ltree_out);
 Datum
 ltree_out(PG_FUNCTION_ARGS)
 {
 	ltree	   *in = PG_GETARG_LTREE_P(0);
-	char	   *buf,
-			   *ptr;
-	int			i;
-	ltree_level *curlevel;
-
-	ptr = buf = (char *) palloc(VARSIZE(in));
-	curlevel = LTREE_FIRST(in);
-	for (i = 0; i < in->numlevel; i++)
-	{
-		if (i != 0)
-		{
-			*ptr = '.';
-			ptr++;
-		}
-		memcpy(ptr, curlevel->name, curlevel->len);
-		ptr += curlevel->len;
-		curlevel = LEVEL_NEXT(curlevel);
-	}
-
-	*ptr = '\0';
+    char       *buf = deparse_ltree(in);
 	PG_FREE_IF_COPY(in, 0);
 
 	PG_RETURN_POINTER(buf);
 }
 
+/*
+ * ltree type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltree_send);
+Datum
+ltree_send(PG_FUNCTION_ARGS)
+{
+    ltree	   *in = PG_GETARG_LTREE_P(0);
+    StringInfoData buf;
+    int         version = 1;
+    char	   *res = deparse_ltree(in);
+
+    pq_begintypsend(&buf);
+    pq_sendint8(&buf, version);
+    pq_sendtext(&buf, res, pg_mbstrlen(res));
+    pfree(res);
+
+    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+/*
+ * ltree type recv function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltree_recv);
+Datum
+ltree_recv(PG_FUNCTION_ARGS)
+{
+    StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
+    int         version = pq_getmsgint(buf, 1);
+    char       *str;
+    int         nbytes;
+    ltree	   *res;
+
+    if (version == 1)
+        str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+    else
+        elog(ERROR, "unsupported ltree version number %d", version);
+
+    res = parse_ltree(str);
+    pfree(str);
+
+    PG_RETURN_POINTER(res);
+}
+
 #define LQPRS_WAITLEVEL 0
 #define LQPRS_WAITDELIM 1
 #define LQPRS_WAITOPEN	2
@@ -190,431 +263,509 @@ ltree_out(PG_FUNCTION_ARGS)
 #define ITEMSIZE	MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
 #define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) )
 
+/*
+ * expects a null terminated string
+ * returns an lquery
+ */
+static lquery*
+parse_lquery(const char* buf)
+{
+    const char	   *ptr;
+    int			num = 0,
+            totallen = 0,
+            numOR = 0;
+    int			state = LQPRS_WAITLEVEL;
+    lquery	   *result;
+    nodeitem   *lptr = NULL;
+    lquery_level *cur,
+            *curqlevel,
+            *tmpql;
+    lquery_variant *lrptr = NULL;
+    bool		hasnot = false;
+    bool		wasbad = false;
+    int			charlen;
+    int			pos = 0;
+
+    ptr = buf;
+    while (*ptr)
+    {
+        charlen = pg_mblen(ptr);
+
+        if (charlen == 1)
+        {
+            if (t_iseq(ptr, '.'))
+                num++;
+            else if (t_iseq(ptr, '|'))
+                numOR++;
+        }
+
+        ptr += charlen;
+    }
+
+    num++;
+    if (num > MaxAllocSize / ITEMSIZE)
+        ereport(ERROR,
+                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                        errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
+                               num, (int) (MaxAllocSize / ITEMSIZE))));
+    curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
+    ptr = buf;
+    while (*ptr)
+    {
+        charlen = pg_mblen(ptr);
+
+        if (state == LQPRS_WAITLEVEL)
+        {
+            if (ISALNUM(ptr))
+            {
+                GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
+                lptr->start = ptr;
+                state = LQPRS_WAITDELIM;
+                curqlevel->numvar = 1;
+            }
+            else if (charlen == 1 && t_iseq(ptr, '!'))
+            {
+                GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
+                lptr->start = ptr + 1;
+                state = LQPRS_WAITDELIM;
+                curqlevel->numvar = 1;
+                curqlevel->flag |= LQL_NOT;
+                hasnot = true;
+            }
+            else if (charlen == 1 && t_iseq(ptr, '*'))
+                state = LQPRS_WAITOPEN;
+            else
+                UNCHAR;
+        }
+        else if (state == LQPRS_WAITVAR)
+        {
+            if (ISALNUM(ptr))
+            {
+                lptr++;
+                lptr->start = ptr;
+                state = LQPRS_WAITDELIM;
+                curqlevel->numvar++;
+            }
+            else
+                UNCHAR;
+        }
+        else if (state == LQPRS_WAITDELIM)
+        {
+            if (charlen == 1 && t_iseq(ptr, '@'))
+            {
+                if (lptr->start == ptr)
+                    UNCHAR;
+                lptr->flag |= LVAR_INCASE;
+                curqlevel->flag |= LVAR_INCASE;
+            }
+            else if (charlen == 1 && t_iseq(ptr, '*'))
+            {
+                if (lptr->start == ptr)
+                    UNCHAR;
+                lptr->flag |= LVAR_ANYEND;
+                curqlevel->flag |= LVAR_ANYEND;
+            }
+            else if (charlen == 1 && t_iseq(ptr, '%'))
+            {
+                if (lptr->start == ptr)
+                    UNCHAR;
+                lptr->flag |= LVAR_SUBLEXEME;
+                curqlevel->flag |= LVAR_SUBLEXEME;
+            }
+            else if (charlen == 1 && t_iseq(ptr, '|'))
+            {
+                lptr->len = ptr - lptr->start -
+                            ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) -
+                            ((lptr->flag & LVAR_INCASE) ? 1 : 0) -
+                            ((lptr->flag & LVAR_ANYEND) ? 1 : 0);
+                if (lptr->wlen > 255)
+                    ereport(ERROR,
+                            (errcode(ERRCODE_NAME_TOO_LONG),
+                                    errmsg("name of level is too long"),
+                                    errdetail("Name length is %d, must "
+                                              "be < 256, in position %d.",
+                                              lptr->wlen, pos)));
+
+                state = LQPRS_WAITVAR;
+            }
+            else if (charlen == 1 && t_iseq(ptr, '.'))
+            {
+                lptr->len = ptr - lptr->start -
+                            ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) -
+                            ((lptr->flag & LVAR_INCASE) ? 1 : 0) -
+                            ((lptr->flag & LVAR_ANYEND) ? 1 : 0);
+                if (lptr->wlen > 255)
+                    ereport(ERROR,
+                            (errcode(ERRCODE_NAME_TOO_LONG),
+                                    errmsg("name of level is too long"),
+                                    errdetail("Name length is %d, must "
+                                              "be < 256, in position %d.",
+                                              lptr->wlen, pos)));
+
+                state = LQPRS_WAITLEVEL;
+                curqlevel = NEXTLEV(curqlevel);
+            }
+            else if (ISALNUM(ptr))
+            {
+                if (lptr->flag)
+                    UNCHAR;
+            }
+            else
+                UNCHAR;
+        }
+        else if (state == LQPRS_WAITOPEN)
+        {
+            if (charlen == 1 && t_iseq(ptr, '{'))
+                state = LQPRS_WAITFNUM;
+            else if (charlen == 1 && t_iseq(ptr, '.'))
+            {
+                curqlevel->low = 0;
+                curqlevel->high = 0xffff;
+                curqlevel = NEXTLEV(curqlevel);
+                state = LQPRS_WAITLEVEL;
+            }
+            else
+                UNCHAR;
+        }
+        else if (state == LQPRS_WAITFNUM)
+        {
+            if (charlen == 1 && t_iseq(ptr, ','))
+                state = LQPRS_WAITSNUM;
+            else if (t_isdigit(ptr))
+            {
+                curqlevel->low = atoi(ptr);
+                state = LQPRS_WAITND;
+            }
+            else
+                UNCHAR;
+        }
+        else if (state == LQPRS_WAITSNUM)
+        {
+            if (t_isdigit(ptr))
+            {
+                curqlevel->high = atoi(ptr);
+                state = LQPRS_WAITCLOSE;
+            }
+            else if (charlen == 1 && t_iseq(ptr, '}'))
+            {
+                curqlevel->high = 0xffff;
+                state = LQPRS_WAITEND;
+            }
+            else
+                UNCHAR;
+        }
+        else if (state == LQPRS_WAITCLOSE)
+        {
+            if (charlen == 1 && t_iseq(ptr, '}'))
+                state = LQPRS_WAITEND;
+            else if (!t_isdigit(ptr))
+                UNCHAR;
+        }
+        else if (state == LQPRS_WAITND)
+        {
+            if (charlen == 1 && t_iseq(ptr, '}'))
+            {
+                curqlevel->high = curqlevel->low;
+                state = LQPRS_WAITEND;
+            }
+            else if (charlen == 1 && t_iseq(ptr, ','))
+                state = LQPRS_WAITSNUM;
+            else if (!t_isdigit(ptr))
+                UNCHAR;
+        }
+        else if (state == LQPRS_WAITEND)
+        {
+            if (charlen == 1 && t_iseq(ptr, '.'))
+            {
+                state = LQPRS_WAITLEVEL;
+                curqlevel = NEXTLEV(curqlevel);
+            }
+            else
+                UNCHAR;
+        }
+        else
+            /* internal error */
+            elog(ERROR, "internal error in parser");
+
+        ptr += charlen;
+        if (state == LQPRS_WAITDELIM)
+            lptr->wlen++;
+        pos++;
+    }
+
+    if (state == LQPRS_WAITDELIM)
+    {
+        if (lptr->start == ptr)
+            ereport(ERROR,
+                    (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("syntax error"),
+                            errdetail("Unexpected end of line.")));
+
+        lptr->len = ptr - lptr->start -
+                    ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) -
+                    ((lptr->flag & LVAR_INCASE) ? 1 : 0) -
+                    ((lptr->flag & LVAR_ANYEND) ? 1 : 0);
+        if (lptr->len == 0)
+            ereport(ERROR,
+                    (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("syntax error"),
+                            errdetail("Unexpected end of line.")));
+
+        if (lptr->wlen > 255)
+            ereport(ERROR,
+                    (errcode(ERRCODE_NAME_TOO_LONG),
+                            errmsg("name of level is too long"),
+                            errdetail("Name length is %d, must "
+                                      "be < 256, in position %d.",
+                                      lptr->wlen, pos)));
+    }
+    else if (state == LQPRS_WAITOPEN)
+        curqlevel->high = 0xffff;
+    else if (state != LQPRS_WAITEND)
+        ereport(ERROR,
+                (errcode(ERRCODE_SYNTAX_ERROR),
+                        errmsg("syntax error"),
+                        errdetail("Unexpected end of line.")));
+
+    curqlevel = tmpql;
+    totallen = LQUERY_HDRSIZE;
+    while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
+    {
+        totallen += LQL_HDRSIZE;
+        if (curqlevel->numvar)
+        {
+            lptr = GETVAR(curqlevel);
+            while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
+            {
+                totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
+                lptr++;
+            }
+        }
+        else if (curqlevel->low > curqlevel->high)
+            ereport(ERROR,
+                    (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("syntax error"),
+                            errdetail("Low limit(%d) is greater than upper(%d).",
+                                      curqlevel->low, curqlevel->high)));
+
+        curqlevel = NEXTLEV(curqlevel);
+    }
+
+    result = (lquery *) palloc0(totallen);
+    SET_VARSIZE(result, totallen);
+    result->numlevel = num;
+    result->firstgood = 0;
+    result->flag = 0;
+    if (hasnot)
+        result->flag |= LQUERY_HASNOT;
+    cur = LQUERY_FIRST(result);
+    curqlevel = tmpql;
+    while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
+    {
+        memcpy(cur, curqlevel, LQL_HDRSIZE);
+        cur->totallen = LQL_HDRSIZE;
+        if (curqlevel->numvar)
+        {
+            lrptr = LQL_FIRST(cur);
+            lptr = GETVAR(curqlevel);
+            while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
+            {
+                cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
+                lrptr->len = lptr->len;
+                lrptr->flag = lptr->flag;
+                lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
+                memcpy(lrptr->name, lptr->start, lptr->len);
+                lptr++;
+                lrptr = LVAR_NEXT(lrptr);
+            }
+            pfree(GETVAR(curqlevel));
+            if (cur->numvar > 1 || cur->flag != 0)
+                wasbad = true;
+            else if (wasbad == false)
+                (result->firstgood)++;
+        }
+        else
+            wasbad = true;
+        curqlevel = NEXTLEV(curqlevel);
+        cur = LQL_NEXT(cur);
+    }
+
+    pfree(tmpql);
+    return result;
+}
+
+/*
+ * expects an lquery
+ * returns a null terminated string
+ */
+static char*
+deparse_lquery(const lquery* in)
+{
+    char	   *buf,
+            *ptr;
+    int			i,
+            j,
+            totallen = 1;
+    lquery_level *curqlevel;
+    lquery_variant *curtlevel;
+
+    curqlevel = LQUERY_FIRST(in);
+    for (i = 0; i < in->numlevel; i++)
+    {
+        totallen++;
+        if (curqlevel->numvar)
+            totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
+        else
+            totallen += 2 * 11 + 4;
+        curqlevel = LQL_NEXT(curqlevel);
+    }
+
+    ptr = buf = (char *) palloc(totallen);
+    curqlevel = LQUERY_FIRST(in);
+    for (i = 0; i < in->numlevel; i++)
+    {
+        if (i != 0)
+        {
+            *ptr = '.';
+            ptr++;
+        }
+        if (curqlevel->numvar)
+        {
+            if (curqlevel->flag & LQL_NOT)
+            {
+                *ptr = '!';
+                ptr++;
+            }
+            curtlevel = LQL_FIRST(curqlevel);
+            for (j = 0; j < curqlevel->numvar; j++)
+            {
+                if (j != 0)
+                {
+                    *ptr = '|';
+                    ptr++;
+                }
+                memcpy(ptr, curtlevel->name, curtlevel->len);
+                ptr += curtlevel->len;
+                if ((curtlevel->flag & LVAR_SUBLEXEME))
+                {
+                    *ptr = '%';
+                    ptr++;
+                }
+                if ((curtlevel->flag & LVAR_INCASE))
+                {
+                    *ptr = '@';
+                    ptr++;
+                }
+                if ((curtlevel->flag & LVAR_ANYEND))
+                {
+                    *ptr = '*';
+                    ptr++;
+                }
+                curtlevel = LVAR_NEXT(curtlevel);
+            }
+        }
+        else
+        {
+            if (curqlevel->low == curqlevel->high)
+            {
+                sprintf(ptr, "*{%d}", curqlevel->low);
+            }
+            else if (curqlevel->low == 0)
+            {
+                if (curqlevel->high == 0xffff)
+                {
+                    *ptr = '*';
+                    *(ptr + 1) = '\0';
+                }
+                else
+                    sprintf(ptr, "*{,%d}", curqlevel->high);
+            }
+            else if (curqlevel->high == 0xffff)
+            {
+                sprintf(ptr, "*{%d,}", curqlevel->low);
+            }
+            else
+                sprintf(ptr, "*{%d,%d}", curqlevel->low, curqlevel->high);
+            ptr = strchr(ptr, '\0');
+        }
+
+        curqlevel = LQL_NEXT(curqlevel);
+    }
+
+    *ptr = '\0';
+    return buf;
+}
+
+PG_FUNCTION_INFO_V1(lquery_in);
 Datum
 lquery_in(PG_FUNCTION_ARGS)
 {
 	char	   *buf = (char *) PG_GETARG_POINTER(0);
-	char	   *ptr;
-	int			num = 0,
-				totallen = 0,
-				numOR = 0;
-	int			state = LQPRS_WAITLEVEL;
-	lquery	   *result;
-	nodeitem   *lptr = NULL;
-	lquery_level *cur,
-			   *curqlevel,
-			   *tmpql;
-	lquery_variant *lrptr = NULL;
-	bool		hasnot = false;
-	bool		wasbad = false;
-	int			charlen;
-	int			pos = 0;
-
-	ptr = buf;
-	while (*ptr)
-	{
-		charlen = pg_mblen(ptr);
-
-		if (charlen == 1)
-		{
-			if (t_iseq(ptr, '.'))
-				num++;
-			else if (t_iseq(ptr, '|'))
-				numOR++;
-		}
-
-		ptr += charlen;
-	}
-
-	num++;
-	if (num > MaxAllocSize / ITEMSIZE)
-		ereport(ERROR,
-				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-				 errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
-						num, (int) (MaxAllocSize / ITEMSIZE))));
-	curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
-	ptr = buf;
-	while (*ptr)
-	{
-		charlen = pg_mblen(ptr);
-
-		if (state == LQPRS_WAITLEVEL)
-		{
-			if (ISALNUM(ptr))
-			{
-				GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
-				lptr->start = ptr;
-				state = LQPRS_WAITDELIM;
-				curqlevel->numvar = 1;
-			}
-			else if (charlen == 1 && t_iseq(ptr, '!'))
-			{
-				GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
-				lptr->start = ptr + 1;
-				state = LQPRS_WAITDELIM;
-				curqlevel->numvar = 1;
-				curqlevel->flag |= LQL_NOT;
-				hasnot = true;
-			}
-			else if (charlen == 1 && t_iseq(ptr, '*'))
-				state = LQPRS_WAITOPEN;
-			else
-				UNCHAR;
-		}
-		else if (state == LQPRS_WAITVAR)
-		{
-			if (ISALNUM(ptr))
-			{
-				lptr++;
-				lptr->start = ptr;
-				state = LQPRS_WAITDELIM;
-				curqlevel->numvar++;
-			}
-			else
-				UNCHAR;
-		}
-		else if (state == LQPRS_WAITDELIM)
-		{
-			if (charlen == 1 && t_iseq(ptr, '@'))
-			{
-				if (lptr->start == ptr)
-					UNCHAR;
-				lptr->flag |= LVAR_INCASE;
-				curqlevel->flag |= LVAR_INCASE;
-			}
-			else if (charlen == 1 && t_iseq(ptr, '*'))
-			{
-				if (lptr->start == ptr)
-					UNCHAR;
-				lptr->flag |= LVAR_ANYEND;
-				curqlevel->flag |= LVAR_ANYEND;
-			}
-			else if (charlen == 1 && t_iseq(ptr, '%'))
-			{
-				if (lptr->start == ptr)
-					UNCHAR;
-				lptr->flag |= LVAR_SUBLEXEME;
-				curqlevel->flag |= LVAR_SUBLEXEME;
-			}
-			else if (charlen == 1 && t_iseq(ptr, '|'))
-			{
-				lptr->len = ptr - lptr->start -
-					((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) -
-					((lptr->flag & LVAR_INCASE) ? 1 : 0) -
-					((lptr->flag & LVAR_ANYEND) ? 1 : 0);
-				if (lptr->wlen > 255)
-					ereport(ERROR,
-							(errcode(ERRCODE_NAME_TOO_LONG),
-							 errmsg("name of level is too long"),
-							 errdetail("Name length is %d, must "
-									   "be < 256, in position %d.",
-									   lptr->wlen, pos)));
-
-				state = LQPRS_WAITVAR;
-			}
-			else if (charlen == 1 && t_iseq(ptr, '.'))
-			{
-				lptr->len = ptr - lptr->start -
-					((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) -
-					((lptr->flag & LVAR_INCASE) ? 1 : 0) -
-					((lptr->flag & LVAR_ANYEND) ? 1 : 0);
-				if (lptr->wlen > 255)
-					ereport(ERROR,
-							(errcode(ERRCODE_NAME_TOO_LONG),
-							 errmsg("name of level is too long"),
-							 errdetail("Name length is %d, must "
-									   "be < 256, in position %d.",
-									   lptr->wlen, pos)));
-
-				state = LQPRS_WAITLEVEL;
-				curqlevel = NEXTLEV(curqlevel);
-			}
-			else if (ISALNUM(ptr))
-			{
-				if (lptr->flag)
-					UNCHAR;
-			}
-			else
-				UNCHAR;
-		}
-		else if (state == LQPRS_WAITOPEN)
-		{
-			if (charlen == 1 && t_iseq(ptr, '{'))
-				state = LQPRS_WAITFNUM;
-			else if (charlen == 1 && t_iseq(ptr, '.'))
-			{
-				curqlevel->low = 0;
-				curqlevel->high = 0xffff;
-				curqlevel = NEXTLEV(curqlevel);
-				state = LQPRS_WAITLEVEL;
-			}
-			else
-				UNCHAR;
-		}
-		else if (state == LQPRS_WAITFNUM)
-		{
-			if (charlen == 1 && t_iseq(ptr, ','))
-				state = LQPRS_WAITSNUM;
-			else if (t_isdigit(ptr))
-			{
-				curqlevel->low = atoi(ptr);
-				state = LQPRS_WAITND;
-			}
-			else
-				UNCHAR;
-		}
-		else if (state == LQPRS_WAITSNUM)
-		{
-			if (t_isdigit(ptr))
-			{
-				curqlevel->high = atoi(ptr);
-				state = LQPRS_WAITCLOSE;
-			}
-			else if (charlen == 1 && t_iseq(ptr, '}'))
-			{
-				curqlevel->high = 0xffff;
-				state = LQPRS_WAITEND;
-			}
-			else
-				UNCHAR;
-		}
-		else if (state == LQPRS_WAITCLOSE)
-		{
-			if (charlen == 1 && t_iseq(ptr, '}'))
-				state = LQPRS_WAITEND;
-			else if (!t_isdigit(ptr))
-				UNCHAR;
-		}
-		else if (state == LQPRS_WAITND)
-		{
-			if (charlen == 1 && t_iseq(ptr, '}'))
-			{
-				curqlevel->high = curqlevel->low;
-				state = LQPRS_WAITEND;
-			}
-			else if (charlen == 1 && t_iseq(ptr, ','))
-				state = LQPRS_WAITSNUM;
-			else if (!t_isdigit(ptr))
-				UNCHAR;
-		}
-		else if (state == LQPRS_WAITEND)
-		{
-			if (charlen == 1 && t_iseq(ptr, '.'))
-			{
-				state = LQPRS_WAITLEVEL;
-				curqlevel = NEXTLEV(curqlevel);
-			}
-			else
-				UNCHAR;
-		}
-		else
-			/* internal error */
-			elog(ERROR, "internal error in parser");
-
-		ptr += charlen;
-		if (state == LQPRS_WAITDELIM)
-			lptr->wlen++;
-		pos++;
-	}
-
-	if (state == LQPRS_WAITDELIM)
-	{
-		if (lptr->start == ptr)
-			ereport(ERROR,
-					(errcode(ERRCODE_SYNTAX_ERROR),
-					 errmsg("syntax error"),
-					 errdetail("Unexpected end of line.")));
-
-		lptr->len = ptr - lptr->start -
-			((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) -
-			((lptr->flag & LVAR_INCASE) ? 1 : 0) -
-			((lptr->flag & LVAR_ANYEND) ? 1 : 0);
-		if (lptr->len == 0)
-			ereport(ERROR,
-					(errcode(ERRCODE_SYNTAX_ERROR),
-					 errmsg("syntax error"),
-					 errdetail("Unexpected end of line.")));
-
-		if (lptr->wlen > 255)
-			ereport(ERROR,
-					(errcode(ERRCODE_NAME_TOO_LONG),
-					 errmsg("name of level is too long"),
-					 errdetail("Name length is %d, must "
-							   "be < 256, in position %d.",
-							   lptr->wlen, pos)));
-	}
-	else if (state == LQPRS_WAITOPEN)
-		curqlevel->high = 0xffff;
-	else if (state != LQPRS_WAITEND)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("syntax error"),
-				 errdetail("Unexpected end of line.")));
-
-	curqlevel = tmpql;
-	totallen = LQUERY_HDRSIZE;
-	while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
-	{
-		totallen += LQL_HDRSIZE;
-		if (curqlevel->numvar)
-		{
-			lptr = GETVAR(curqlevel);
-			while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
-			{
-				totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
-				lptr++;
-			}
-		}
-		else if (curqlevel->low > curqlevel->high)
-			ereport(ERROR,
-					(errcode(ERRCODE_SYNTAX_ERROR),
-					 errmsg("syntax error"),
-					 errdetail("Low limit(%d) is greater than upper(%d).",
-							   curqlevel->low, curqlevel->high)));
-
-		curqlevel = NEXTLEV(curqlevel);
-	}
-
-	result = (lquery *) palloc0(totallen);
-	SET_VARSIZE(result, totallen);
-	result->numlevel = num;
-	result->firstgood = 0;
-	result->flag = 0;
-	if (hasnot)
-		result->flag |= LQUERY_HASNOT;
-	cur = LQUERY_FIRST(result);
-	curqlevel = tmpql;
-	while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
-	{
-		memcpy(cur, curqlevel, LQL_HDRSIZE);
-		cur->totallen = LQL_HDRSIZE;
-		if (curqlevel->numvar)
-		{
-			lrptr = LQL_FIRST(cur);
-			lptr = GETVAR(curqlevel);
-			while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
-			{
-				cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
-				lrptr->len = lptr->len;
-				lrptr->flag = lptr->flag;
-				lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
-				memcpy(lrptr->name, lptr->start, lptr->len);
-				lptr++;
-				lrptr = LVAR_NEXT(lrptr);
-			}
-			pfree(GETVAR(curqlevel));
-			if (cur->numvar > 1 || cur->flag != 0)
-				wasbad = true;
-			else if (wasbad == false)
-				(result->firstgood)++;
-		}
-		else
-			wasbad = true;
-		curqlevel = NEXTLEV(curqlevel);
-		cur = LQL_NEXT(cur);
-	}
-
-	pfree(tmpql);
+	lquery     *result = parse_lquery(buf);
 	PG_RETURN_POINTER(result);
 }
 
+PG_FUNCTION_INFO_V1(lquery_out);
 Datum
 lquery_out(PG_FUNCTION_ARGS)
 {
 	lquery	   *in = PG_GETARG_LQUERY_P(0);
-	char	   *buf,
-			   *ptr;
-	int			i,
-				j,
-				totallen = 1;
-	lquery_level *curqlevel;
-	lquery_variant *curtlevel;
-
-	curqlevel = LQUERY_FIRST(in);
-	for (i = 0; i < in->numlevel; i++)
-	{
-		totallen++;
-		if (curqlevel->numvar)
-			totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
-		else
-			totallen += 2 * 11 + 4;
-		curqlevel = LQL_NEXT(curqlevel);
-	}
-
-	ptr = buf = (char *) palloc(totallen);
-	curqlevel = LQUERY_FIRST(in);
-	for (i = 0; i < in->numlevel; i++)
-	{
-		if (i != 0)
-		{
-			*ptr = '.';
-			ptr++;
-		}
-		if (curqlevel->numvar)
-		{
-			if (curqlevel->flag & LQL_NOT)
-			{
-				*ptr = '!';
-				ptr++;
-			}
-			curtlevel = LQL_FIRST(curqlevel);
-			for (j = 0; j < curqlevel->numvar; j++)
-			{
-				if (j != 0)
-				{
-					*ptr = '|';
-					ptr++;
-				}
-				memcpy(ptr, curtlevel->name, curtlevel->len);
-				ptr += curtlevel->len;
-				if ((curtlevel->flag & LVAR_SUBLEXEME))
-				{
-					*ptr = '%';
-					ptr++;
-				}
-				if ((curtlevel->flag & LVAR_INCASE))
-				{
-					*ptr = '@';
-					ptr++;
-				}
-				if ((curtlevel->flag & LVAR_ANYEND))
-				{
-					*ptr = '*';
-					ptr++;
-				}
-				curtlevel = LVAR_NEXT(curtlevel);
-			}
-		}
-		else
-		{
-			if (curqlevel->low == curqlevel->high)
-			{
-				sprintf(ptr, "*{%d}", curqlevel->low);
-			}
-			else if (curqlevel->low == 0)
-			{
-				if (curqlevel->high == 0xffff)
-				{
-					*ptr = '*';
-					*(ptr + 1) = '\0';
-				}
-				else
-					sprintf(ptr, "*{,%d}", curqlevel->high);
-			}
-			else if (curqlevel->high == 0xffff)
-			{
-				sprintf(ptr, "*{%d,}", curqlevel->low);
-			}
-			else
-				sprintf(ptr, "*{%d,%d}", curqlevel->low, curqlevel->high);
-			ptr = strchr(ptr, '\0');
-		}
-
-		curqlevel = LQL_NEXT(curqlevel);
-	}
-
-	*ptr = '\0';
-	PG_FREE_IF_COPY(in, 0);
+    char       *buf = deparse_lquery(in);
+    PG_FREE_IF_COPY(in, 0);
 
 	PG_RETURN_POINTER(buf);
 }
+
+/*
+ * lquery type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(lquery_send);
+Datum
+lquery_send(PG_FUNCTION_ARGS)
+{
+    lquery	   *in = PG_GETARG_LQUERY_P(0);
+    StringInfoData buf;
+    int         version = 1;
+    char	   *res = deparse_lquery(in);
+
+    pq_begintypsend(&buf);
+    pq_sendint8(&buf, version);
+    pq_sendtext(&buf, res, pg_mbstrlen(res));
+    pfree(res);
+
+    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+/*
+ * lquery type recv function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(lquery_recv);
+Datum
+lquery_recv(PG_FUNCTION_ARGS)
+{
+    StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
+    int         version = pq_getmsgint(buf, 1);
+    char       *str;
+    int         nbytes;
+    lquery	   *res;
+
+    if (version == 1)
+        str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+    else
+        elog(ERROR, "unsupported lquery version number %d", version);
+
+    res = parse_lquery(str);
+    pfree(str);
+
+    PG_RETURN_POINTER(res);
+}
\ No newline at end of file
diff --git a/contrib/ltree/ltxtquery_io.c b/contrib/ltree/ltxtquery_io.c
index 56bf39d145..1e349ae8f8 100644
--- a/contrib/ltree/ltxtquery_io.c
+++ b/contrib/ltree/ltxtquery_io.c
@@ -4,6 +4,7 @@
  * contrib/ltree/ltxtquery_io.c
  */
 #include "postgres.h"
+#include "libpq/pqformat.h"
 
 #include <ctype.h>
 
@@ -11,10 +12,6 @@
 #include "ltree.h"
 #include "miscadmin.h"
 
-PG_FUNCTION_INFO_V1(ltxtq_in);
-PG_FUNCTION_INFO_V1(ltxtq_out);
-
-
 /* parser's states */
 #define WAITOPERAND 1
 #define INOPERAND 2
@@ -382,12 +379,42 @@ queryin(char *buf)
 /*
  * in without morphology
  */
+PG_FUNCTION_INFO_V1(ltxtq_in);
 Datum
 ltxtq_in(PG_FUNCTION_ARGS)
 {
 	PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0)));
 }
 
+/*
+ * ltxtquery type recv function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltxtq_recv);
+Datum
+ltxtq_recv(PG_FUNCTION_ARGS)
+{
+    StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
+    int         version = pq_getmsgint(buf, 1);
+    char        *str;
+    int         nbytes;
+    ltxtquery	*res;
+
+    if (version == 1)
+        str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+    else
+        elog(ERROR, "unsupported ltxtquery version number %d", version);
+
+    res = queryin(str);
+    pfree(str);
+
+    PG_RETURN_POINTER(res);
+}
+
 /*
  * out function
  */
@@ -512,6 +539,7 @@ infix(INFIX *in, bool first)
 	}
 }
 
+PG_FUNCTION_INFO_V1(ltxtq_out);
 Datum
 ltxtq_out(PG_FUNCTION_ARGS)
 {
@@ -534,3 +562,42 @@ ltxtq_out(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 0);
 	PG_RETURN_POINTER(nrm.buf);
 }
+
+/*
+ * ltxtquery type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltxtq_send);
+Datum
+ltxtq_send(PG_FUNCTION_ARGS)
+{
+    ltxtquery  *query = PG_GETARG_LTXTQUERY_P(0);
+    StringInfoData buf;
+    int         version = 1;
+    INFIX		nrm;
+
+    if (query->size == 0)
+        ereport(ERROR,
+                (errcode(ERRCODE_SYNTAX_ERROR),
+                        errmsg("syntax error"),
+                        errdetail("Empty query.")));
+
+    nrm.curpol = GETQUERY(query);
+    nrm.buflen = 32;
+    nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
+    *(nrm.cur) = '\0';
+    nrm.op = GETOPERAND(query);
+    infix(&nrm, true);
+
+    PG_FREE_IF_COPY(query, 0);
+    pq_begintypsend(&buf);
+    pq_sendint8(&buf, version);
+    pq_sendtext(&buf, nrm.buf, pg_mbstrlen(nrm.buf));
+    pfree(nrm.buf);
+
+    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
\ No newline at end of file
-- 
2.21.0

#2Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Nino Floris (#1)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

Hi!

On Sun, Aug 18, 2019 at 6:53 PM Nino Floris <mail@ninofloris.com> wrote:

Attached is a patch to support send/recv on ltree, lquery and ltxtquery.
I'm a contributor to the Npgsql .NET PostgreSQL driver and we'll be
the first to have official support once those ltree changes have been
released.
You can find the driver support work here, the tests verify a
roundtrip for each of the types is succesful.
https://github.com/NinoFloris/npgsql/tree/label-tree-support

Thank you for the patch.

My notes are following:
* Your patch doesn't follow coding convention. In particular, it
uses spaces for indentation instead of tabs. Moreover, it changes
tags to spaces in places it doesn't touch. As the result, patch is
"dirty". Looks like your IDE isn't properly setup. Please check your
patch doesn't contain unintended changes and follow coding convention.
It's also good practice to run pgindent over changed files before
sending patch.
* We currently don't add new extension SQL-script for new extension
version (i.e. ltree--1.2.sql). Instead, we provide just a migration
script (i.e. ltree--1.1--1.2.sql). This simplifies testing of
extension migration – plain extension creation implies migration.
* What is motivation for binary format for lquery and ltxtquery? One
could transfer large datasets of ltree's. But is it so for lquery and
ltxtquery, which are used just for querying.
* Just send binary representation of datatype is not OK. PostgreSQL
supports a lot of platforms with different byte ordering, alignment
and so on. You basically need to send each particular field using
pq_send*() function.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#3Nino Floris
mail@ninofloris.com
In reply to: Alexander Korotkov (#2)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

Hi Alexander,

Thanks for the initial review!

* Your patch doesn't follow coding convention. In particular, it
uses spaces for indentation instead of tabs. Moreover, it changes
tags to spaces in places it doesn't touch. As the result, patch is
"dirty". Looks like your IDE isn't properly setup. Please check your
patch doesn't contain unintended changes and follow coding convention.
It's also good practice to run pgindent over changed files before
sending patch.

Apologies, will fix, I indeed haven't seen the requirement being tabs.
Though not to sound pedantic I would expect an .editorconfig for such
(industry) non standard indenting as it's broadly supported and very
little effort to do so.
I will run pgindent, thanks for the pointer, that looks super useful.

* We currently don't add new extension SQL-script for new extension
version (i.e. ltree--1.2.sql). Instead, we provide just a migration
script (i.e. ltree--1.1--1.2.sql). This simplifies testing of
extension migration – plain extension creation implies migration.

I wasn't sure how to add new methods to the type without doing a full
CREATE TYPE, as ALTER TYPE doesn't allow updates to functions. At that
point wouldn't it be better as a new version?
I checked some other extensions like hstore to find reference material
how to do a new CREATE TYPE, all did a full version bump.
Should I just do a DROP and CREATE instead in a migration?

* What is motivation for binary format for lquery and ltxtquery? One

could transfer large datasets of ltree's. But is it so for lquery and
ltxtquery, which are used just for querying.

Completeness, Npgsql (and other drivers like Ecto from Elixir and
probably many others as well) can't do any text fallback in the binary
protocol without manual configuration.
This is because these drivers don't know much (or anything) about the
SQL they send so it wouldn't know to apply it for which columns.
I believe there has been a proposal at some point to enhance the
binary protocol to additionally allow clients to specify text/binary
per data type instead of per column.
That would allow all these drivers to automate this, but I think it
never went anywhere.
As it stands it's not ergonomic to ask developers to setup this
metadata per query they write.

* Just send binary representation of datatype is not OK. PostgreSQL
supports a lot of platforms with different byte ordering, alignment
and so on. You basically need to send each particular field using
pq_send*() function.

Oh my, I don't think I did? I copied what jsonb is doing,
specifically, sending the textual representation of the data type with
a version field prefixed.
(It is why I introduced deparse_ltree/lquery, to take the respective
structure and create a null terminated string of its textual form)
So there are no other fields besides version and the string
representation of the structure.
ltree, lquery, and ltxtquery all seem to have a lossless and compact
textual interpretation.
My motivation here has been "if it's good enough for jsonb it should
be good enough for a niche extension like ltree" especially as having
any binary support is better than not having it at all.
I can change it to anything you'd like to see instead but I would need
some pointers as to what that would be.

Again, thank you for taking up this patch to review.

Best regards,
Nino Floris

On Mon, Sep 9, 2019 at 11:38 PM Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

Show quoted text

Hi!

On Sun, Aug 18, 2019 at 6:53 PM Nino Floris <mail@ninofloris.com> wrote:

Attached is a patch to support send/recv on ltree, lquery and ltxtquery.
I'm a contributor to the Npgsql .NET PostgreSQL driver and we'll be
the first to have official support once those ltree changes have been
released.
You can find the driver support work here, the tests verify a
roundtrip for each of the types is succesful.
https://github.com/NinoFloris/npgsql/tree/label-tree-support

Thank you for the patch.

My notes are following:
* Your patch doesn't follow coding convention. In particular, it
uses spaces for indentation instead of tabs. Moreover, it changes
tags to spaces in places it doesn't touch. As the result, patch is
"dirty". Looks like your IDE isn't properly setup. Please check your
patch doesn't contain unintended changes and follow coding convention.
It's also good practice to run pgindent over changed files before
sending patch.
* We currently don't add new extension SQL-script for new extension
version (i.e. ltree--1.2.sql). Instead, we provide just a migration
script (i.e. ltree--1.1--1.2.sql). This simplifies testing of
extension migration – plain extension creation implies migration.
* What is motivation for binary format for lquery and ltxtquery? One
could transfer large datasets of ltree's. But is it so for lquery and
ltxtquery, which are used just for querying.
* Just send binary representation of datatype is not OK. PostgreSQL
supports a lot of platforms with different byte ordering, alignment
and so on. You basically need to send each particular field using
pq_send*() function.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#4Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Nino Floris (#3)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

On Wed, Sep 11, 2019 at 9:45 PM Nino Floris <mail@ninofloris.com> wrote:

* We currently don't add new extension SQL-script for new extension
version (i.e. ltree--1.2.sql). Instead, we provide just a migration
script (i.e. ltree--1.1--1.2.sql). This simplifies testing of
extension migration – plain extension creation implies migration.

I wasn't sure how to add new methods to the type without doing a full
CREATE TYPE, as ALTER TYPE doesn't allow updates to functions. At that
point wouldn't it be better as a new version?
I checked some other extensions like hstore to find reference material
how to do a new CREATE TYPE, all did a full version bump.
Should I just do a DROP and CREATE instead in a migration?

Oh, I appears we didn't add send/recv to any pluggable datatype one we
get extension system.

Ideally we need to adjust ALTER TYPE command so that it could add
send/recv functions to an existing type.

I see couple other options:
1) Write migration script, which directly updates pg_type.
2) Add ltree--1.2.sql and advise users to recreate extension to get
binary protocol support.
But both these options are cumbersome.

What do you think about writing patch for ALTER TYPE?

* What is motivation for binary format for lquery and ltxtquery? One

could transfer large datasets of ltree's. But is it so for lquery and
ltxtquery, which are used just for querying.

Completeness, Npgsql (and other drivers like Ecto from Elixir and
probably many others as well) can't do any text fallback in the binary
protocol without manual configuration.
This is because these drivers don't know much (or anything) about the
SQL they send so it wouldn't know to apply it for which columns.
I believe there has been a proposal at some point to enhance the
binary protocol to additionally allow clients to specify text/binary
per data type instead of per column.
That would allow all these drivers to automate this, but I think it
never went anywhere.
As it stands it's not ergonomic to ask developers to setup this
metadata per query they write.

* Just send binary representation of datatype is not OK. PostgreSQL
supports a lot of platforms with different byte ordering, alignment
and so on. You basically need to send each particular field using
pq_send*() function.

Oh my, I don't think I did? I copied what jsonb is doing,
specifically, sending the textual representation of the data type with
a version field prefixed.
(It is why I introduced deparse_ltree/lquery, to take the respective
structure and create a null terminated string of its textual form)
So there are no other fields besides version and the string
representation of the structure.
ltree, lquery, and ltxtquery all seem to have a lossless and compact
textual interpretation.
My motivation here has been "if it's good enough for jsonb it should
be good enough for a niche extension like ltree" especially as having
any binary support is better than not having it at all.
I can change it to anything you'd like to see instead but I would need
some pointers as to what that would be.

Oh, sorry. I didn't notice you send textual representation for ltree,
lquery, and ltxtquery. And the point is not performance for these
datatypes, but completeness. Now I got it, then it looks OK.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#5Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Nino Floris (#3)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

Hello, can you please post an updated version of this patch? Note that
Travis has a small complaint:

gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -O2 -Wall -Werror -fPIC -DLOWER_NODE -I. -I. -I../../src/include -I/usr/include/x86_64-linux-gnu -D_GNU_SOURCE -c -o crc32.o crc32.c
crc32.c: In function ‘ltree_crc32_sz’:
crc32.c:26:12: error: initialization discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
char *p = buf;
^
cc1: all warnings being treated as errors
<builtin>: recipe for target 'crc32.o' failed
make[4]: *** [crc32.o] Error 1
make[4]: Leaving directory '/home/travis/build/postgresql-cfbot/postgresql/contrib/ltree'

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#6Nino Floris
mail@ninofloris.com
In reply to: Alvaro Herrera (#5)
1 attachment(s)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

Alright, as usual life got in the way. I've attached a new version of
the patch with pgindent changes.

What do you think about writing patch for ALTER TYPE?

I'd rather not :$

1) Write migration script, which directly updates pg_type.

This sounds like the best option right now, if we don't want people to
do manual migrations to ltree 1.2.
How would I best go at this?

Travis has a small complaint:

Should be fixed!

Nino Floris

On Wed, Sep 25, 2019 at 10:31 PM Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Show quoted text

Hello, can you please post an updated version of this patch? Note that
Travis has a small complaint:

gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -O2 -Wall -Werror -fPIC -DLOWER_NODE -I. -I. -I../../src/include -I/usr/include/x86_64-linux-gnu -D_GNU_SOURCE -c -o crc32.o crc32.c
crc32.c: In function ‘ltree_crc32_sz’:
crc32.c:26:12: error: initialization discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
char *p = buf;
^
cc1: all warnings being treated as errors
<builtin>: recipe for target 'crc32.o' failed
make[4]: *** [crc32.o] Error 1
make[4]: Leaving directory '/home/travis/build/postgresql-cfbot/postgresql/contrib/ltree'

--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

0002-Implements-ltree-lquery-and-ltxtquery-binary-protoco.patchapplication/octet-stream; name=0002-Implements-ltree-lquery-and-ltxtquery-binary-protoco.patchDownload
From bfe6b7c2b794446c348a712efd0aa9ce59130d63 Mon Sep 17 00:00:00 2001
From: Nino Floris <mail@ninofloris.com>
Date: Sun, 18 Aug 2019 16:30:30 +0200
Subject: [PATCH] Implements ltree, lquery, and ltxtquery binary protocol
 support

---
 contrib/ltree/Makefile       |   2 +-
 contrib/ltree/crc32.c        |   4 +-
 contrib/ltree/crc32.h        |   2 +-
 contrib/ltree/lquery_op.c    |   2 +-
 contrib/ltree/ltree--1.2.sql | 907 +++++++++++++++++++++++++++++++++++
 contrib/ltree/ltree.control  |   2 +-
 contrib/ltree/ltree.h        |   8 +-
 contrib/ltree/ltree_io.c     | 203 +++++++-
 contrib/ltree/ltxtquery_io.c |  75 ++-
 9 files changed, 1167 insertions(+), 38 deletions(-)
 create mode 100644 contrib/ltree/ltree--1.2.sql

diff --git a/contrib/ltree/Makefile b/contrib/ltree/Makefile
index 416c8da312..2a17593212 100644
--- a/contrib/ltree/Makefile
+++ b/contrib/ltree/Makefile
@@ -6,7 +6,7 @@ OBJS = 	ltree_io.o ltree_op.o lquery_op.o _ltree_op.o crc32.o \
 PG_CPPFLAGS = -DLOWER_NODE
 
 EXTENSION = ltree
-DATA = ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
+DATA = ltree--1.2.sql ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
 PGFILEDESC = "ltree - hierarchical label data type"
 
 HEADERS = ltree.h
diff --git a/contrib/ltree/crc32.c b/contrib/ltree/crc32.c
index 447e4b2960..1debbf9d3a 100644
--- a/contrib/ltree/crc32.c
+++ b/contrib/ltree/crc32.c
@@ -20,10 +20,10 @@
 #include "crc32.h"
 
 unsigned int
-ltree_crc32_sz(char *buf, int size)
+ltree_crc32_sz(const char *buf, int size)
 {
 	pg_crc32	crc;
-	char	   *p = buf;
+	const char *p = buf;
 
 	INIT_TRADITIONAL_CRC32(crc);
 	while (size > 0)
diff --git a/contrib/ltree/crc32.h b/contrib/ltree/crc32.h
index 269d05d0c1..958812214d 100644
--- a/contrib/ltree/crc32.h
+++ b/contrib/ltree/crc32.h
@@ -4,7 +4,7 @@
 /* contrib/ltree/crc32.h */
 
 /* Returns crc32 of data block */
-extern unsigned int ltree_crc32_sz(char *buf, int size);
+extern unsigned int ltree_crc32_sz(const char *buf, int size);
 
 /* Returns crc32 of null-terminated string */
 #define crc32(buf) ltree_crc32_sz((buf),strlen(buf))
diff --git a/contrib/ltree/lquery_op.c b/contrib/ltree/lquery_op.c
index b6d2deb1af..62172d5ea1 100644
--- a/contrib/ltree/lquery_op.c
+++ b/contrib/ltree/lquery_op.c
@@ -50,7 +50,7 @@ getlexeme(char *start, char *end, int *len)
 }
 
 bool
-			compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend)
+compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend)
 {
 	char	   *endt = t->name + t->len;
 	char	   *endq = qn + len;
diff --git a/contrib/ltree/ltree--1.2.sql b/contrib/ltree/ltree--1.2.sql
new file mode 100644
index 0000000000..ab762597e4
--- /dev/null
+++ b/contrib/ltree/ltree--1.2.sql
@@ -0,0 +1,907 @@
+/* contrib/ltree/ltree--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION ltree" to load this file. \quit
+
+CREATE FUNCTION ltree_in(cstring)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_out(ltree)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_recv(internal)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_send(ltree)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE ltree (
+        INTERNALLENGTH = -1,
+        INPUT = ltree_in,
+        OUTPUT = ltree_out,
+        RECEIVE = ltree_recv,
+        SEND = ltree_send,
+        STORAGE = extended
+);
+
+--Compare function for ltree
+CREATE FUNCTION ltree_cmp(ltree,ltree)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_lt(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_le(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_eq(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_ge(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_gt(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_ne(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+
+CREATE OPERATOR < (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_lt,
+        COMMUTATOR = '>',
+	NEGATOR = '>=',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <= (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_le,
+        COMMUTATOR = '>=',
+	NEGATOR = '>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR >= (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_ge,
+        COMMUTATOR = '<=',
+	NEGATOR = '<',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR > (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_gt,
+        COMMUTATOR = '<',
+	NEGATOR = '<=',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR = (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_eq,
+        COMMUTATOR = '=',
+	NEGATOR = '<>',
+        RESTRICT = eqsel,
+	JOIN = eqjoinsel,
+        SORT1 = '<',
+	SORT2 = '<'
+);
+
+CREATE OPERATOR <> (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_ne,
+        COMMUTATOR = '<>',
+	NEGATOR = '=',
+        RESTRICT = neqsel,
+	JOIN = neqjoinsel
+);
+
+--util functions
+
+CREATE FUNCTION subltree(ltree,int4,int4)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subpath(ltree,int4,int4)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subpath(ltree,int4)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION index(ltree,ltree)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'ltree_index'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION index(ltree,ltree,int4)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'ltree_index'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION nlevel(ltree)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree2text(ltree)
+RETURNS text
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION text2ltree(text)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(_ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME','_lca'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_isparent(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_risparent(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_addltree(ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_addtext(ltree,text)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_textadd(text,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltreeparentsel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR @> (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_isparent,
+        COMMUTATOR = '<@',
+        RESTRICT = ltreeparentsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@> (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_isparent,
+        COMMUTATOR = '^<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_risparent,
+        COMMUTATOR = '@>',
+        RESTRICT = ltreeparentsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^<@ (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_risparent,
+        COMMUTATOR = '^@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR || (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_addltree
+);
+
+CREATE OPERATOR || (
+        LEFTARG = ltree,
+	RIGHTARG = text,
+	PROCEDURE = ltree_addtext
+);
+
+CREATE OPERATOR || (
+        LEFTARG = text,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_textadd
+);
+
+
+-- B-tree support
+
+CREATE OPERATOR CLASS ltree_ops
+    DEFAULT FOR TYPE ltree USING btree AS
+        OPERATOR        1       < ,
+        OPERATOR        2       <= ,
+        OPERATOR        3       = ,
+        OPERATOR        4       >= ,
+        OPERATOR        5       > ,
+        FUNCTION        1       ltree_cmp(ltree, ltree);
+
+
+--lquery type
+CREATE FUNCTION lquery_in(cstring)
+RETURNS lquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lquery_out(lquery)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lquery_recv(internal)
+RETURNS lquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lquery_send(lquery)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE lquery (
+	INTERNALLENGTH = -1,
+	INPUT = lquery_in,
+	OUTPUT = lquery_out,
+    RECEIVE = lquery_recv,
+    SEND = lquery_send,
+	STORAGE = extended
+);
+
+CREATE FUNCTION ltq_regex(ltree,lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltq_rregex(lquery,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ~ (
+        LEFTARG = ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = ltq_regex,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+        LEFTARG = lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltq_rregex,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--not-indexed
+CREATE OPERATOR ^~ (
+        LEFTARG = ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = ltq_regex,
+	COMMUTATOR = '^~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^~ (
+        LEFTARG = lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltq_rregex,
+	COMMUTATOR = '^~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION lt_q_regex(ltree,_lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lt_q_rregex(_lquery,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ? (
+        LEFTARG = ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = lt_q_regex,
+	COMMUTATOR = '?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ? (
+        LEFTARG = _lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = lt_q_rregex,
+	COMMUTATOR = '?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--not-indexed
+CREATE OPERATOR ^? (
+        LEFTARG = ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = lt_q_regex,
+	COMMUTATOR = '^?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^? (
+        LEFTARG = _lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = lt_q_rregex,
+	COMMUTATOR = '^?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION ltxtq_in(cstring)
+RETURNS ltxtquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_out(ltxtquery)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_recv(internal)
+RETURNS ltxtquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_send(ltxtquery)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE ltxtquery (
+	INTERNALLENGTH = -1,
+	INPUT = ltxtq_in,
+	OUTPUT = ltxtq_out,
+	RECEIVE = ltxtq_recv,
+	SEND = ltxtq_send,
+	STORAGE = extended
+);
+
+-- operations WITH ltxtquery
+
+CREATE FUNCTION ltxtq_exec(ltree, ltxtquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_rexec(ltxtquery, ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR @ (
+        LEFTARG = ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = ltxtq_exec,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltxtq_rexec,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--not-indexed
+CREATE OPERATOR ^@ (
+        LEFTARG = ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = ltxtq_exec,
+	COMMUTATOR = '^@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltxtq_rexec,
+	COMMUTATOR = '^@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--GiST support for ltree
+CREATE FUNCTION ltree_gist_in(cstring)
+RETURNS ltree_gist
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_gist_out(ltree_gist)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE ltree_gist (
+	internallength = -1,
+	input = ltree_gist_in,
+	output = ltree_gist_out,
+	storage = plain
+);
+
+
+CREATE FUNCTION ltree_consistent(internal,ltree,int2,oid,internal)
+RETURNS bool as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_compress(internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_decompress(internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_penalty(internal,internal,internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_picksplit(internal, internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_union(internal, internal)
+RETURNS ltree_gist as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_same(ltree_gist, ltree_gist, internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gist_ltree_ops
+    DEFAULT FOR TYPE ltree USING gist AS
+	OPERATOR	1	< ,
+	OPERATOR	2	<= ,
+	OPERATOR	3	= ,
+	OPERATOR	4	>= ,
+	OPERATOR	5	> ,
+	OPERATOR	10	@> ,
+	OPERATOR	11	<@ ,
+	OPERATOR	12	~ (ltree, lquery) ,
+	OPERATOR	13	~ (lquery, ltree) ,
+	OPERATOR	14	@ (ltree, ltxtquery) ,
+	OPERATOR	15	@ (ltxtquery, ltree) ,
+	OPERATOR	16	? (ltree, _lquery) ,
+	OPERATOR	17	? (_lquery, ltree) ,
+	FUNCTION	1	ltree_consistent (internal, ltree, int2, oid, internal),
+	FUNCTION	2	ltree_union (internal, internal),
+	FUNCTION	3	ltree_compress (internal),
+	FUNCTION	4	ltree_decompress (internal),
+	FUNCTION	5	ltree_penalty (internal, internal, internal),
+	FUNCTION	6	ltree_picksplit (internal, internal),
+	FUNCTION	7	ltree_same (ltree_gist, ltree_gist, internal),
+	STORAGE		ltree_gist;
+
+
+-- arrays of ltree
+
+CREATE FUNCTION _ltree_isparent(_ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_r_isparent(ltree,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_risparent(_ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_r_risparent(ltree,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltq_regex(_ltree,lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltq_rregex(lquery,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _lt_q_regex(_ltree,_lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _lt_q_rregex(_lquery,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltxtq_exec(_ltree, ltxtquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltxtq_rexec(ltxtquery, _ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR @> (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_isparent,
+        COMMUTATOR = '<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_isparent,
+        COMMUTATOR = '@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_risparent,
+        COMMUTATOR = '@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @> (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_risparent,
+        COMMUTATOR = '<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+        LEFTARG = _ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = _ltq_regex,
+        COMMUTATOR = '~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+        LEFTARG = lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltq_rregex,
+        COMMUTATOR = '~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ? (
+        LEFTARG = _ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = _lt_q_regex,
+        COMMUTATOR = '?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ? (
+        LEFTARG = _lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _lt_q_rregex,
+        COMMUTATOR = '?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = _ltxtq_exec,
+        COMMUTATOR = '@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltxtq_rexec,
+        COMMUTATOR = '@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+
+--not indexed
+CREATE OPERATOR ^@> (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_isparent,
+        COMMUTATOR = '^<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^<@ (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_isparent,
+        COMMUTATOR = '^@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^<@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_risparent,
+        COMMUTATOR = '^@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@> (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_risparent,
+        COMMUTATOR = '^<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^~ (
+        LEFTARG = _ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = _ltq_regex,
+        COMMUTATOR = '^~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^~ (
+        LEFTARG = lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltq_rregex,
+        COMMUTATOR = '^~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^? (
+        LEFTARG = _ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = _lt_q_regex,
+        COMMUTATOR = '^?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^? (
+        LEFTARG = _lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _lt_q_rregex,
+        COMMUTATOR = '^?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = _ltxtq_exec,
+        COMMUTATOR = '^@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltxtq_rexec,
+        COMMUTATOR = '^@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--extractors
+CREATE FUNCTION _ltree_extract_isparent(_ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?@> (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_extract_isparent
+);
+
+CREATE FUNCTION _ltree_extract_risparent(_ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?<@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_extract_risparent
+);
+
+CREATE FUNCTION _ltq_extract_regex(_ltree,lquery)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?~ (
+        LEFTARG = _ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = _ltq_extract_regex
+);
+
+CREATE FUNCTION _ltxtq_extract_exec(_ltree,ltxtquery)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = _ltxtq_extract_exec
+);
+
+--GiST support for ltree[]
+CREATE FUNCTION _ltree_consistent(internal,_ltree,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_union(internal, internal)
+RETURNS ltree_gist
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_same(ltree_gist, ltree_gist, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gist__ltree_ops
+    DEFAULT FOR TYPE _ltree USING gist AS
+	OPERATOR	10	<@ (_ltree, ltree),
+	OPERATOR	11	@> (ltree, _ltree),
+	OPERATOR	12	~ (_ltree, lquery),
+	OPERATOR	13	~ (lquery, _ltree),
+	OPERATOR	14	@ (_ltree, ltxtquery),
+	OPERATOR	15	@ (ltxtquery, _ltree),
+	OPERATOR	16	? (_ltree, _lquery),
+	OPERATOR	17	? (_lquery, _ltree),
+	FUNCTION	1	_ltree_consistent (internal, _ltree, int2, oid, internal),
+	FUNCTION	2	_ltree_union (internal, internal),
+	FUNCTION	3	_ltree_compress (internal),
+	FUNCTION	4	ltree_decompress (internal),
+	FUNCTION	5	_ltree_penalty (internal, internal, internal),
+	FUNCTION	6	_ltree_picksplit (internal, internal),
+	FUNCTION	7	_ltree_same (ltree_gist, ltree_gist, internal),
+	STORAGE		ltree_gist;
diff --git a/contrib/ltree/ltree.control b/contrib/ltree/ltree.control
index 03c3fb1ab5..61c8cdf40a 100644
--- a/contrib/ltree/ltree.control
+++ b/contrib/ltree/ltree.control
@@ -1,5 +1,5 @@
 # ltree extension
 comment = 'data type for hierarchical tree-like structures'
-default_version = '1.1'
+default_version = '1.2'
 module_pathname = '$libdir/ltree'
 relocatable = true
diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h
index e4b8c84fa6..366e58004c 100644
--- a/contrib/ltree/ltree.h
+++ b/contrib/ltree/ltree.h
@@ -155,13 +155,13 @@ Datum		ltree_textadd(PG_FUNCTION_ARGS);
 /* Util function */
 Datum		ltree_in(PG_FUNCTION_ARGS);
 
-bool ltree_execute(ITEM *curitem, void *checkval,
-			  bool calcnot, bool (*chkcond) (void *checkval, ITEM *val));
+bool		ltree_execute(ITEM *curitem, void *checkval,
+						  bool calcnot, bool (*chkcond) (void *checkval, ITEM *val));
 
 int			ltree_compare(const ltree *a, const ltree *b);
 bool		inner_isparent(const ltree *c, const ltree *p);
-bool compare_subnode(ltree_level *t, char *q, int len,
-				int (*cmpptr) (const char *, const char *, size_t), bool anyend);
+bool		compare_subnode(ltree_level *t, char *q, int len,
+							int (*cmpptr) (const char *, const char *, size_t), bool anyend);
 ltree	   *lca_inner(ltree **a, int len);
 int			ltree_strncasecmp(const char *a, const char *b, size_t s);
 
diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c
index f54f037443..b5cca12835 100644
--- a/contrib/ltree/ltree_io.c
+++ b/contrib/ltree/ltree_io.c
@@ -4,28 +4,23 @@
  * contrib/ltree/ltree_io.c
  */
 #include "postgres.h"
+#include "libpq/pqformat.h"
 
 #include <ctype.h>
 
 #include "ltree.h"
 #include "utils/memutils.h"
+#include "utils/builtins.h"
 #include "crc32.h"
 
-PG_FUNCTION_INFO_V1(ltree_in);
-PG_FUNCTION_INFO_V1(ltree_out);
-PG_FUNCTION_INFO_V1(lquery_in);
-PG_FUNCTION_INFO_V1(lquery_out);
-
-
 #define UNCHAR ereport(ERROR, \
 					   (errcode(ERRCODE_SYNTAX_ERROR), \
 						errmsg("syntax error at position %d", \
 						pos)));
 
-
 typedef struct
 {
-	char	   *start;
+	const char *start;
 	int			len;			/* length in bytes */
 	int			flag;
 	int			wlen;			/* length in characters */
@@ -34,11 +29,14 @@ typedef struct
 #define LTPRS_WAITNAME	0
 #define LTPRS_WAITDELIM 1
 
-Datum
-ltree_in(PG_FUNCTION_ARGS)
+/*
+ * expects a null terminated string
+ * returns an ltree
+ */
+static ltree *
+parse_ltree(const char *buf)
 {
-	char	   *buf = (char *) PG_GETARG_POINTER(0);
-	char	   *ptr;
+	const char *ptr;
 	nodeitem   *list,
 			   *lptr;
 	int			num = 0,
@@ -143,13 +141,16 @@ ltree_in(PG_FUNCTION_ARGS)
 	}
 
 	pfree(list);
-	PG_RETURN_POINTER(result);
+	return result;
 }
 
-Datum
-ltree_out(PG_FUNCTION_ARGS)
+/*
+ * expects an ltree
+ * returns a null terminated string
+ */
+static char *
+deparse_ltree(const ltree *in)
 {
-	ltree	   *in = PG_GETARG_LTREE_P(0);
 	char	   *buf,
 			   *ptr;
 	int			i;
@@ -170,11 +171,85 @@ ltree_out(PG_FUNCTION_ARGS)
 	}
 
 	*ptr = '\0';
+	return buf;
+}
+
+PG_FUNCTION_INFO_V1(ltree_in);
+Datum
+ltree_in(PG_FUNCTION_ARGS)
+{
+	char	   *buf = (char *) PG_GETARG_POINTER(0);
+	ltree	   *result = parse_ltree(buf);
+
+	PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(ltree_out);
+Datum
+ltree_out(PG_FUNCTION_ARGS)
+{
+	ltree	   *in = PG_GETARG_LTREE_P(0);
+	char	   *buf = deparse_ltree(in);
+
 	PG_FREE_IF_COPY(in, 0);
 
 	PG_RETURN_POINTER(buf);
 }
 
+/*
+ * ltree type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltree_send);
+Datum
+ltree_send(PG_FUNCTION_ARGS)
+{
+	ltree	   *in = PG_GETARG_LTREE_P(0);
+	StringInfoData buf;
+	int			version = 1;
+	char	   *res = deparse_ltree(in);
+
+	pq_begintypsend(&buf);
+	pq_sendint8(&buf, version);
+	pq_sendtext(&buf, res, pg_mbstrlen(res));
+	pfree(res);
+
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+/*
+ * ltree type recv function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltree_recv);
+Datum
+ltree_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	int			version = pq_getmsgint(buf, 1);
+	char	   *str;
+	int			nbytes;
+	ltree	   *res;
+
+	if (version == 1)
+		str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+	else
+		elog(ERROR, "unsupported ltree version number %d", version);
+
+	res = parse_ltree(str);
+	pfree(str);
+
+	PG_RETURN_POINTER(res);
+}
+
 #define LQPRS_WAITLEVEL 0
 #define LQPRS_WAITDELIM 1
 #define LQPRS_WAITOPEN	2
@@ -190,11 +265,14 @@ ltree_out(PG_FUNCTION_ARGS)
 #define ITEMSIZE	MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
 #define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) )
 
-Datum
-lquery_in(PG_FUNCTION_ARGS)
+/*
+ * expects a null terminated string
+ * returns an lquery
+ */
+static lquery *
+parse_lquery(const char *buf)
 {
-	char	   *buf = (char *) PG_GETARG_POINTER(0);
-	char	   *ptr;
+	const char *ptr;
 	int			num = 0,
 				totallen = 0,
 				numOR = 0;
@@ -515,13 +593,16 @@ lquery_in(PG_FUNCTION_ARGS)
 	}
 
 	pfree(tmpql);
-	PG_RETURN_POINTER(result);
+	return result;
 }
 
-Datum
-lquery_out(PG_FUNCTION_ARGS)
+/*
+ * expects an lquery
+ * returns a null terminated string
+ */
+static char *
+deparse_lquery(const lquery *in)
 {
-	lquery	   *in = PG_GETARG_LQUERY_P(0);
 	char	   *buf,
 			   *ptr;
 	int			i,
@@ -614,7 +695,81 @@ lquery_out(PG_FUNCTION_ARGS)
 	}
 
 	*ptr = '\0';
+	return buf;
+}
+
+PG_FUNCTION_INFO_V1(lquery_in);
+Datum
+lquery_in(PG_FUNCTION_ARGS)
+{
+	char	   *buf = (char *) PG_GETARG_POINTER(0);
+	lquery	   *result = parse_lquery(buf);
+
+	PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(lquery_out);
+Datum
+lquery_out(PG_FUNCTION_ARGS)
+{
+	lquery	   *in = PG_GETARG_LQUERY_P(0);
+	char	   *buf = deparse_lquery(in);
+
 	PG_FREE_IF_COPY(in, 0);
 
 	PG_RETURN_POINTER(buf);
 }
+
+/*
+ * lquery type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(lquery_send);
+Datum
+lquery_send(PG_FUNCTION_ARGS)
+{
+	lquery	   *in = PG_GETARG_LQUERY_P(0);
+	StringInfoData buf;
+	int			version = 1;
+	char	   *res = deparse_lquery(in);
+
+	pq_begintypsend(&buf);
+	pq_sendint8(&buf, version);
+	pq_sendtext(&buf, res, pg_mbstrlen(res));
+	pfree(res);
+
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+/*
+ * lquery type recv function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(lquery_recv);
+Datum
+lquery_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	int			version = pq_getmsgint(buf, 1);
+	char	   *str;
+	int			nbytes;
+	lquery	   *res;
+
+	if (version == 1)
+		str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+	else
+		elog(ERROR, "unsupported lquery version number %d", version);
+
+	res = parse_lquery(str);
+	pfree(str);
+
+	PG_RETURN_POINTER(res);
+}
diff --git a/contrib/ltree/ltxtquery_io.c b/contrib/ltree/ltxtquery_io.c
index 56bf39d145..b2f0081053 100644
--- a/contrib/ltree/ltxtquery_io.c
+++ b/contrib/ltree/ltxtquery_io.c
@@ -4,6 +4,7 @@
  * contrib/ltree/ltxtquery_io.c
  */
 #include "postgres.h"
+#include "libpq/pqformat.h"
 
 #include <ctype.h>
 
@@ -11,10 +12,6 @@
 #include "ltree.h"
 #include "miscadmin.h"
 
-PG_FUNCTION_INFO_V1(ltxtq_in);
-PG_FUNCTION_INFO_V1(ltxtq_out);
-
-
 /* parser's states */
 #define WAITOPERAND 1
 #define INOPERAND 2
@@ -382,12 +379,42 @@ queryin(char *buf)
 /*
  * in without morphology
  */
+PG_FUNCTION_INFO_V1(ltxtq_in);
 Datum
 ltxtq_in(PG_FUNCTION_ARGS)
 {
 	PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0)));
 }
 
+/*
+ * ltxtquery type recv function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltxtq_recv);
+Datum
+ltxtq_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	int			version = pq_getmsgint(buf, 1);
+	char	   *str;
+	int			nbytes;
+	ltxtquery  *res;
+
+	if (version == 1)
+		str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+	else
+		elog(ERROR, "unsupported ltxtquery version number %d", version);
+
+	res = queryin(str);
+	pfree(str);
+
+	PG_RETURN_POINTER(res);
+}
+
 /*
  * out function
  */
@@ -512,6 +539,7 @@ infix(INFIX *in, bool first)
 	}
 }
 
+PG_FUNCTION_INFO_V1(ltxtq_out);
 Datum
 ltxtq_out(PG_FUNCTION_ARGS)
 {
@@ -534,3 +562,42 @@ ltxtq_out(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 0);
 	PG_RETURN_POINTER(nrm.buf);
 }
+
+/*
+ * ltxtquery type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltxtq_send);
+Datum
+ltxtq_send(PG_FUNCTION_ARGS)
+{
+	ltxtquery  *query = PG_GETARG_LTXTQUERY_P(0);
+	StringInfoData buf;
+	int			version = 1;
+	INFIX		nrm;
+
+	if (query->size == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("syntax error"),
+				 errdetail("Empty query.")));
+
+	nrm.curpol = GETQUERY(query);
+	nrm.buflen = 32;
+	nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
+	*(nrm.cur) = '\0';
+	nrm.op = GETOPERAND(query);
+	infix(&nrm, true);
+
+	PG_FREE_IF_COPY(query, 0);
+	pq_begintypsend(&buf);
+	pq_sendint8(&buf, version);
+	pq_sendtext(&buf, nrm.buf, pg_mbstrlen(nrm.buf));
+	pfree(nrm.buf);
+
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
-- 
2.23.0

#7Michael Paquier
michael@paquier.xyz
In reply to: Nino Floris (#6)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

On Mon, Nov 11, 2019 at 03:44:54PM +0100, Nino Floris wrote:

Alright, as usual life got in the way. I've attached a new version of
the patch with pgindent changes.

What do you think about writing patch for ALTER TYPE?

I'd rather not :$

1) Write migration script, which directly updates pg_type.

This sounds like the best option right now, if we don't want people to
do manual migrations to ltree 1.2.
How would I best go at this?

Travis has a small complaint:

Should be fixed!

The latest patch provided fails to apply for me on HEAD. Please
provide a rebase. For now I am bumping this patch to next CF with
"waiting on author" as status.
--
Michael

#8Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Michael Paquier (#7)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

On Fri, Nov 29, 2019 at 11:29:03AM +0900, Michael Paquier wrote:

On Mon, Nov 11, 2019 at 03:44:54PM +0100, Nino Floris wrote:

Alright, as usual life got in the way. I've attached a new version of
the patch with pgindent changes.

What do you think about writing patch for ALTER TYPE?

I'd rather not :$

1) Write migration script, which directly updates pg_type.

This sounds like the best option right now, if we don't want people to
do manual migrations to ltree 1.2.
How would I best go at this?

Travis has a small complaint:

Should be fixed!

The latest patch provided fails to apply for me on HEAD. Please
provide a rebase. For now I am bumping this patch to next CF with
"waiting on author" as status.

Nino, any plans to submit a rebased/fixed patch, so that people can
review it? Not sure if this needs a simple rebase or something more
complex, all I know is cputube can't apply it.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#9Nino Floris
mail@ninofloris.com
In reply to: Tomas Vondra (#8)
1 attachment(s)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

Attached is the new patch rebased onto master.

Best regards,
Nino Floris

On Thu, Jan 16, 2020 at 11:00 PM Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:

Show quoted text

On Fri, Nov 29, 2019 at 11:29:03AM +0900, Michael Paquier wrote:

On Mon, Nov 11, 2019 at 03:44:54PM +0100, Nino Floris wrote:

Alright, as usual life got in the way. I've attached a new version of
the patch with pgindent changes.

What do you think about writing patch for ALTER TYPE?

I'd rather not :$

1) Write migration script, which directly updates pg_type.

This sounds like the best option right now, if we don't want people to
do manual migrations to ltree 1.2.
How would I best go at this?

Travis has a small complaint:

Should be fixed!

The latest patch provided fails to apply for me on HEAD. Please
provide a rebase. For now I am bumping this patch to next CF with
"waiting on author" as status.

Nino, any plans to submit a rebased/fixed patch, so that people can
review it? Not sure if this needs a simple rebase or something more
complex, all I know is cputube can't apply it.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

0003-Implements-ltree-lquery-and-ltxtquery-binary-protoco.patchapplication/octet-stream; name=0003-Implements-ltree-lquery-and-ltxtquery-binary-protoco.patchDownload
From 8b961d6eaa6bc3068f044030ba4e3fff50d8b471 Mon Sep 17 00:00:00 2001
From: Nino Floris <mail@ninofloris.com>
Date: Sun, 18 Aug 2019 16:30:30 +0200
Subject: [PATCH] Implements ltree, lquery, and ltxtquery binary protocol
 support

---
 contrib/ltree/Makefile       |   2 +-
 contrib/ltree/crc32.c        |   4 +-
 contrib/ltree/crc32.h        |   2 +-
 contrib/ltree/ltree--1.2.sql | 907 +++++++++++++++++++++++++++++++++++
 contrib/ltree/ltree.control  |   2 +-
 contrib/ltree/ltree_io.c     | 203 +++++++-
 contrib/ltree/ltxtquery_io.c |  75 ++-
 7 files changed, 1162 insertions(+), 33 deletions(-)
 create mode 100644 contrib/ltree/ltree--1.2.sql

diff --git a/contrib/ltree/Makefile b/contrib/ltree/Makefile
index 31a1be0d3e..cc0d2c09e9 100644
--- a/contrib/ltree/Makefile
+++ b/contrib/ltree/Makefile
@@ -15,7 +15,7 @@ OBJS = \
 PG_CPPFLAGS = -DLOWER_NODE
 
 EXTENSION = ltree
-DATA = ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
+DATA = ltree--1.2.sql ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
 PGFILEDESC = "ltree - hierarchical label data type"
 
 HEADERS = ltree.h
diff --git a/contrib/ltree/crc32.c b/contrib/ltree/crc32.c
index 0c3e45923b..8fed3346e8 100644
--- a/contrib/ltree/crc32.c
+++ b/contrib/ltree/crc32.c
@@ -20,10 +20,10 @@
 #include "utils/pg_crc.h"
 
 unsigned int
-ltree_crc32_sz(char *buf, int size)
+ltree_crc32_sz(const char *buf, int size)
 {
 	pg_crc32	crc;
-	char	   *p = buf;
+	const char *p = buf;
 
 	INIT_TRADITIONAL_CRC32(crc);
 	while (size > 0)
diff --git a/contrib/ltree/crc32.h b/contrib/ltree/crc32.h
index 269d05d0c1..958812214d 100644
--- a/contrib/ltree/crc32.h
+++ b/contrib/ltree/crc32.h
@@ -4,7 +4,7 @@
 /* contrib/ltree/crc32.h */
 
 /* Returns crc32 of data block */
-extern unsigned int ltree_crc32_sz(char *buf, int size);
+extern unsigned int ltree_crc32_sz(const char *buf, int size);
 
 /* Returns crc32 of null-terminated string */
 #define crc32(buf) ltree_crc32_sz((buf),strlen(buf))
diff --git a/contrib/ltree/ltree--1.2.sql b/contrib/ltree/ltree--1.2.sql
new file mode 100644
index 0000000000..ab762597e4
--- /dev/null
+++ b/contrib/ltree/ltree--1.2.sql
@@ -0,0 +1,907 @@
+/* contrib/ltree/ltree--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION ltree" to load this file. \quit
+
+CREATE FUNCTION ltree_in(cstring)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_out(ltree)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_recv(internal)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_send(ltree)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE ltree (
+        INTERNALLENGTH = -1,
+        INPUT = ltree_in,
+        OUTPUT = ltree_out,
+        RECEIVE = ltree_recv,
+        SEND = ltree_send,
+        STORAGE = extended
+);
+
+--Compare function for ltree
+CREATE FUNCTION ltree_cmp(ltree,ltree)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_lt(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_le(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_eq(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_ge(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_gt(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_ne(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+
+CREATE OPERATOR < (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_lt,
+        COMMUTATOR = '>',
+	NEGATOR = '>=',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <= (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_le,
+        COMMUTATOR = '>=',
+	NEGATOR = '>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR >= (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_ge,
+        COMMUTATOR = '<=',
+	NEGATOR = '<',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR > (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_gt,
+        COMMUTATOR = '<',
+	NEGATOR = '<=',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR = (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_eq,
+        COMMUTATOR = '=',
+	NEGATOR = '<>',
+        RESTRICT = eqsel,
+	JOIN = eqjoinsel,
+        SORT1 = '<',
+	SORT2 = '<'
+);
+
+CREATE OPERATOR <> (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_ne,
+        COMMUTATOR = '<>',
+	NEGATOR = '=',
+        RESTRICT = neqsel,
+	JOIN = neqjoinsel
+);
+
+--util functions
+
+CREATE FUNCTION subltree(ltree,int4,int4)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subpath(ltree,int4,int4)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subpath(ltree,int4)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION index(ltree,ltree)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'ltree_index'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION index(ltree,ltree,int4)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'ltree_index'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION nlevel(ltree)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree2text(ltree)
+RETURNS text
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION text2ltree(text)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(_ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME','_lca'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_isparent(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_risparent(ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_addltree(ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_addtext(ltree,text)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_textadd(text,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltreeparentsel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR @> (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_isparent,
+        COMMUTATOR = '<@',
+        RESTRICT = ltreeparentsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@> (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_isparent,
+        COMMUTATOR = '^<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_risparent,
+        COMMUTATOR = '@>',
+        RESTRICT = ltreeparentsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^<@ (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_risparent,
+        COMMUTATOR = '^@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR || (
+        LEFTARG = ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_addltree
+);
+
+CREATE OPERATOR || (
+        LEFTARG = ltree,
+	RIGHTARG = text,
+	PROCEDURE = ltree_addtext
+);
+
+CREATE OPERATOR || (
+        LEFTARG = text,
+	RIGHTARG = ltree,
+	PROCEDURE = ltree_textadd
+);
+
+
+-- B-tree support
+
+CREATE OPERATOR CLASS ltree_ops
+    DEFAULT FOR TYPE ltree USING btree AS
+        OPERATOR        1       < ,
+        OPERATOR        2       <= ,
+        OPERATOR        3       = ,
+        OPERATOR        4       >= ,
+        OPERATOR        5       > ,
+        FUNCTION        1       ltree_cmp(ltree, ltree);
+
+
+--lquery type
+CREATE FUNCTION lquery_in(cstring)
+RETURNS lquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lquery_out(lquery)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lquery_recv(internal)
+RETURNS lquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lquery_send(lquery)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE lquery (
+	INTERNALLENGTH = -1,
+	INPUT = lquery_in,
+	OUTPUT = lquery_out,
+    RECEIVE = lquery_recv,
+    SEND = lquery_send,
+	STORAGE = extended
+);
+
+CREATE FUNCTION ltq_regex(ltree,lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltq_rregex(lquery,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ~ (
+        LEFTARG = ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = ltq_regex,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+        LEFTARG = lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltq_rregex,
+	COMMUTATOR = '~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--not-indexed
+CREATE OPERATOR ^~ (
+        LEFTARG = ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = ltq_regex,
+	COMMUTATOR = '^~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^~ (
+        LEFTARG = lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltq_rregex,
+	COMMUTATOR = '^~',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION lt_q_regex(ltree,_lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION lt_q_rregex(_lquery,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ? (
+        LEFTARG = ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = lt_q_regex,
+	COMMUTATOR = '?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ? (
+        LEFTARG = _lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = lt_q_rregex,
+	COMMUTATOR = '?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--not-indexed
+CREATE OPERATOR ^? (
+        LEFTARG = ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = lt_q_regex,
+	COMMUTATOR = '^?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^? (
+        LEFTARG = _lquery,
+	RIGHTARG = ltree,
+	PROCEDURE = lt_q_rregex,
+	COMMUTATOR = '^?',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE FUNCTION ltxtq_in(cstring)
+RETURNS ltxtquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_out(ltxtquery)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_recv(internal)
+RETURNS ltxtquery
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_send(ltxtquery)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE ltxtquery (
+	INTERNALLENGTH = -1,
+	INPUT = ltxtq_in,
+	OUTPUT = ltxtq_out,
+	RECEIVE = ltxtq_recv,
+	SEND = ltxtq_send,
+	STORAGE = extended
+);
+
+-- operations WITH ltxtquery
+
+CREATE FUNCTION ltxtq_exec(ltree, ltxtquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltxtq_rexec(ltxtquery, ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR @ (
+        LEFTARG = ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = ltxtq_exec,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltxtq_rexec,
+	COMMUTATOR = '@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--not-indexed
+CREATE OPERATOR ^@ (
+        LEFTARG = ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = ltxtq_exec,
+	COMMUTATOR = '^@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = ltree,
+	PROCEDURE = ltxtq_rexec,
+	COMMUTATOR = '^@',
+	RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--GiST support for ltree
+CREATE FUNCTION ltree_gist_in(cstring)
+RETURNS ltree_gist
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION ltree_gist_out(ltree_gist)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE ltree_gist (
+	internallength = -1,
+	input = ltree_gist_in,
+	output = ltree_gist_out,
+	storage = plain
+);
+
+
+CREATE FUNCTION ltree_consistent(internal,ltree,int2,oid,internal)
+RETURNS bool as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_compress(internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_decompress(internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_penalty(internal,internal,internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_picksplit(internal, internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_union(internal, internal)
+RETURNS ltree_gist as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ltree_same(ltree_gist, ltree_gist, internal)
+RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gist_ltree_ops
+    DEFAULT FOR TYPE ltree USING gist AS
+	OPERATOR	1	< ,
+	OPERATOR	2	<= ,
+	OPERATOR	3	= ,
+	OPERATOR	4	>= ,
+	OPERATOR	5	> ,
+	OPERATOR	10	@> ,
+	OPERATOR	11	<@ ,
+	OPERATOR	12	~ (ltree, lquery) ,
+	OPERATOR	13	~ (lquery, ltree) ,
+	OPERATOR	14	@ (ltree, ltxtquery) ,
+	OPERATOR	15	@ (ltxtquery, ltree) ,
+	OPERATOR	16	? (ltree, _lquery) ,
+	OPERATOR	17	? (_lquery, ltree) ,
+	FUNCTION	1	ltree_consistent (internal, ltree, int2, oid, internal),
+	FUNCTION	2	ltree_union (internal, internal),
+	FUNCTION	3	ltree_compress (internal),
+	FUNCTION	4	ltree_decompress (internal),
+	FUNCTION	5	ltree_penalty (internal, internal, internal),
+	FUNCTION	6	ltree_picksplit (internal, internal),
+	FUNCTION	7	ltree_same (ltree_gist, ltree_gist, internal),
+	STORAGE		ltree_gist;
+
+
+-- arrays of ltree
+
+CREATE FUNCTION _ltree_isparent(_ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_r_isparent(ltree,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_risparent(_ltree,ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_r_risparent(ltree,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltq_regex(_ltree,lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltq_rregex(lquery,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _lt_q_regex(_ltree,_lquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _lt_q_rregex(_lquery,_ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltxtq_exec(_ltree, ltxtquery)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltxtq_rexec(ltxtquery, _ltree)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR @> (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_isparent,
+        COMMUTATOR = '<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_isparent,
+        COMMUTATOR = '@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_risparent,
+        COMMUTATOR = '@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @> (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_risparent,
+        COMMUTATOR = '<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+        LEFTARG = _ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = _ltq_regex,
+        COMMUTATOR = '~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+        LEFTARG = lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltq_rregex,
+        COMMUTATOR = '~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ? (
+        LEFTARG = _ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = _lt_q_regex,
+        COMMUTATOR = '?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ? (
+        LEFTARG = _lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _lt_q_rregex,
+        COMMUTATOR = '?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = _ltxtq_exec,
+        COMMUTATOR = '@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR @ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltxtq_rexec,
+        COMMUTATOR = '@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+
+--not indexed
+CREATE OPERATOR ^@> (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_isparent,
+        COMMUTATOR = '^<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^<@ (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_isparent,
+        COMMUTATOR = '^@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^<@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_risparent,
+        COMMUTATOR = '^@>',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@> (
+        LEFTARG = ltree,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltree_r_risparent,
+        COMMUTATOR = '^<@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^~ (
+        LEFTARG = _ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = _ltq_regex,
+        COMMUTATOR = '^~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^~ (
+        LEFTARG = lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltq_rregex,
+        COMMUTATOR = '^~',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^? (
+        LEFTARG = _ltree,
+	RIGHTARG = _lquery,
+	PROCEDURE = _lt_q_regex,
+        COMMUTATOR = '^?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^? (
+        LEFTARG = _lquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _lt_q_rregex,
+        COMMUTATOR = '^?',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = _ltxtq_exec,
+        COMMUTATOR = '^@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ^@ (
+        LEFTARG = ltxtquery,
+	RIGHTARG = _ltree,
+	PROCEDURE = _ltxtq_rexec,
+        COMMUTATOR = '^@',
+        RESTRICT = contsel,
+	JOIN = contjoinsel
+);
+
+--extractors
+CREATE FUNCTION _ltree_extract_isparent(_ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?@> (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_extract_isparent
+);
+
+CREATE FUNCTION _ltree_extract_risparent(_ltree,ltree)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?<@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltree,
+	PROCEDURE = _ltree_extract_risparent
+);
+
+CREATE FUNCTION _ltq_extract_regex(_ltree,lquery)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?~ (
+        LEFTARG = _ltree,
+	RIGHTARG = lquery,
+	PROCEDURE = _ltq_extract_regex
+);
+
+CREATE FUNCTION _ltxtq_extract_exec(_ltree,ltxtquery)
+RETURNS ltree
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR ?@ (
+        LEFTARG = _ltree,
+	RIGHTARG = ltxtquery,
+	PROCEDURE = _ltxtq_extract_exec
+);
+
+--GiST support for ltree[]
+CREATE FUNCTION _ltree_consistent(internal,_ltree,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_union(internal, internal)
+RETURNS ltree_gist
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_same(ltree_gist, ltree_gist, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gist__ltree_ops
+    DEFAULT FOR TYPE _ltree USING gist AS
+	OPERATOR	10	<@ (_ltree, ltree),
+	OPERATOR	11	@> (ltree, _ltree),
+	OPERATOR	12	~ (_ltree, lquery),
+	OPERATOR	13	~ (lquery, _ltree),
+	OPERATOR	14	@ (_ltree, ltxtquery),
+	OPERATOR	15	@ (ltxtquery, _ltree),
+	OPERATOR	16	? (_ltree, _lquery),
+	OPERATOR	17	? (_lquery, _ltree),
+	FUNCTION	1	_ltree_consistent (internal, _ltree, int2, oid, internal),
+	FUNCTION	2	_ltree_union (internal, internal),
+	FUNCTION	3	_ltree_compress (internal),
+	FUNCTION	4	ltree_decompress (internal),
+	FUNCTION	5	_ltree_penalty (internal, internal, internal),
+	FUNCTION	6	_ltree_picksplit (internal, internal),
+	FUNCTION	7	_ltree_same (ltree_gist, ltree_gist, internal),
+	STORAGE		ltree_gist;
diff --git a/contrib/ltree/ltree.control b/contrib/ltree/ltree.control
index 03c3fb1ab5..61c8cdf40a 100644
--- a/contrib/ltree/ltree.control
+++ b/contrib/ltree/ltree.control
@@ -1,5 +1,5 @@
 # ltree extension
 comment = 'data type for hierarchical tree-like structures'
-default_version = '1.1'
+default_version = '1.2'
 module_pathname = '$libdir/ltree'
 relocatable = true
diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c
index 900a46a9e7..b568ae784c 100644
--- a/contrib/ltree/ltree_io.c
+++ b/contrib/ltree/ltree_io.c
@@ -8,24 +8,19 @@
 #include <ctype.h>
 
 #include "crc32.h"
+#include "libpq/pqformat.h"
 #include "ltree.h"
+#include "utils/builtins.h"
 #include "utils/memutils.h"
 
-PG_FUNCTION_INFO_V1(ltree_in);
-PG_FUNCTION_INFO_V1(ltree_out);
-PG_FUNCTION_INFO_V1(lquery_in);
-PG_FUNCTION_INFO_V1(lquery_out);
-
-
 #define UNCHAR ereport(ERROR, \
 					   (errcode(ERRCODE_SYNTAX_ERROR), \
 						errmsg("syntax error at position %d", \
 						pos)));
 
-
 typedef struct
 {
-	char	   *start;
+	const char *start;
 	int			len;			/* length in bytes */
 	int			flag;
 	int			wlen;			/* length in characters */
@@ -34,11 +29,14 @@ typedef struct
 #define LTPRS_WAITNAME	0
 #define LTPRS_WAITDELIM 1
 
-Datum
-ltree_in(PG_FUNCTION_ARGS)
+/*
+ * expects a null terminated string
+ * returns an ltree
+ */
+static ltree *
+parse_ltree(const char *buf)
 {
-	char	   *buf = (char *) PG_GETARG_POINTER(0);
-	char	   *ptr;
+	const char *ptr;
 	nodeitem   *list,
 			   *lptr;
 	int			num = 0,
@@ -143,13 +141,16 @@ ltree_in(PG_FUNCTION_ARGS)
 	}
 
 	pfree(list);
-	PG_RETURN_POINTER(result);
+	return result;
 }
 
-Datum
-ltree_out(PG_FUNCTION_ARGS)
+/*
+ * expects an ltree
+ * returns a null terminated string
+ */
+static char *
+deparse_ltree(const ltree *in)
 {
-	ltree	   *in = PG_GETARG_LTREE_P(0);
 	char	   *buf,
 			   *ptr;
 	int			i;
@@ -170,11 +171,85 @@ ltree_out(PG_FUNCTION_ARGS)
 	}
 
 	*ptr = '\0';
+	return buf;
+}
+
+PG_FUNCTION_INFO_V1(ltree_in);
+Datum
+ltree_in(PG_FUNCTION_ARGS)
+{
+	char	   *buf = (char *) PG_GETARG_POINTER(0);
+	ltree	   *result = parse_ltree(buf);
+
+	PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(ltree_out);
+Datum
+ltree_out(PG_FUNCTION_ARGS)
+{
+	ltree	   *in = PG_GETARG_LTREE_P(0);
+	char	   *buf = deparse_ltree(in);
+
 	PG_FREE_IF_COPY(in, 0);
 
 	PG_RETURN_POINTER(buf);
 }
 
+/*
+ * ltree type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltree_send);
+Datum
+ltree_send(PG_FUNCTION_ARGS)
+{
+	ltree	   *in = PG_GETARG_LTREE_P(0);
+	StringInfoData buf;
+	int			version = 1;
+	char	   *res = deparse_ltree(in);
+
+	pq_begintypsend(&buf);
+	pq_sendint8(&buf, version);
+	pq_sendtext(&buf, res, pg_mbstrlen(res));
+	pfree(res);
+
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+/*
+ * ltree type recv function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltree_recv);
+Datum
+ltree_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	int			version = pq_getmsgint(buf, 1);
+	char	   *str;
+	int			nbytes;
+	ltree	   *res;
+
+	if (version == 1)
+		str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+	else
+		elog(ERROR, "unsupported ltree version number %d", version);
+
+	res = parse_ltree(str);
+	pfree(str);
+
+	PG_RETURN_POINTER(res);
+}
+
 #define LQPRS_WAITLEVEL 0
 #define LQPRS_WAITDELIM 1
 #define LQPRS_WAITOPEN	2
@@ -190,11 +265,14 @@ ltree_out(PG_FUNCTION_ARGS)
 #define ITEMSIZE	MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
 #define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) )
 
-Datum
-lquery_in(PG_FUNCTION_ARGS)
+/*
+ * expects a null terminated string
+ * returns an lquery
+ */
+static lquery *
+parse_lquery(const char *buf)
 {
-	char	   *buf = (char *) PG_GETARG_POINTER(0);
-	char	   *ptr;
+	const char *ptr;
 	int			num = 0,
 				totallen = 0,
 				numOR = 0;
@@ -515,13 +593,16 @@ lquery_in(PG_FUNCTION_ARGS)
 	}
 
 	pfree(tmpql);
-	PG_RETURN_POINTER(result);
+	return result;
 }
 
-Datum
-lquery_out(PG_FUNCTION_ARGS)
+/*
+ * expects an lquery
+ * returns a null terminated string
+ */
+static char *
+deparse_lquery(const lquery *in)
 {
-	lquery	   *in = PG_GETARG_LQUERY_P(0);
 	char	   *buf,
 			   *ptr;
 	int			i,
@@ -614,7 +695,81 @@ lquery_out(PG_FUNCTION_ARGS)
 	}
 
 	*ptr = '\0';
+	return buf;
+}
+
+PG_FUNCTION_INFO_V1(lquery_in);
+Datum
+lquery_in(PG_FUNCTION_ARGS)
+{
+	char	   *buf = (char *) PG_GETARG_POINTER(0);
+	lquery	   *result = parse_lquery(buf);
+
+	PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(lquery_out);
+Datum
+lquery_out(PG_FUNCTION_ARGS)
+{
+	lquery	   *in = PG_GETARG_LQUERY_P(0);
+	char	   *buf = deparse_lquery(in);
+
 	PG_FREE_IF_COPY(in, 0);
 
 	PG_RETURN_POINTER(buf);
 }
+
+/*
+ * lquery type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(lquery_send);
+Datum
+lquery_send(PG_FUNCTION_ARGS)
+{
+	lquery	   *in = PG_GETARG_LQUERY_P(0);
+	StringInfoData buf;
+	int			version = 1;
+	char	   *res = deparse_lquery(in);
+
+	pq_begintypsend(&buf);
+	pq_sendint8(&buf, version);
+	pq_sendtext(&buf, res, pg_mbstrlen(res));
+	pfree(res);
+
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+/*
+ * lquery type recv function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(lquery_recv);
+Datum
+lquery_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	int			version = pq_getmsgint(buf, 1);
+	char	   *str;
+	int			nbytes;
+	lquery	   *res;
+
+	if (version == 1)
+		str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+	else
+		elog(ERROR, "unsupported lquery version number %d", version);
+
+	res = parse_lquery(str);
+	pfree(str);
+
+	PG_RETURN_POINTER(res);
+}
diff --git a/contrib/ltree/ltxtquery_io.c b/contrib/ltree/ltxtquery_io.c
index db347f7772..b02d67bd94 100644
--- a/contrib/ltree/ltxtquery_io.c
+++ b/contrib/ltree/ltxtquery_io.c
@@ -8,13 +8,10 @@
 #include <ctype.h>
 
 #include "crc32.h"
+#include "libpq/pqformat.h"
 #include "ltree.h"
 #include "miscadmin.h"
 
-PG_FUNCTION_INFO_V1(ltxtq_in);
-PG_FUNCTION_INFO_V1(ltxtq_out);
-
-
 /* parser's states */
 #define WAITOPERAND 1
 #define INOPERAND 2
@@ -381,12 +378,42 @@ queryin(char *buf)
 /*
  * in without morphology
  */
+PG_FUNCTION_INFO_V1(ltxtq_in);
 Datum
 ltxtq_in(PG_FUNCTION_ARGS)
 {
 	PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0)));
 }
 
+/*
+ * ltxtquery type recv function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltxtq_recv);
+Datum
+ltxtq_recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	int			version = pq_getmsgint(buf, 1);
+	char	   *str;
+	int			nbytes;
+	ltxtquery  *res;
+
+	if (version == 1)
+		str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+	else
+		elog(ERROR, "unsupported ltxtquery version number %d", version);
+
+	res = queryin(str);
+	pfree(str);
+
+	PG_RETURN_POINTER(res);
+}
+
 /*
  * out function
  */
@@ -511,6 +538,7 @@ infix(INFIX *in, bool first)
 	}
 }
 
+PG_FUNCTION_INFO_V1(ltxtq_out);
 Datum
 ltxtq_out(PG_FUNCTION_ARGS)
 {
@@ -533,3 +561,42 @@ ltxtq_out(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 0);
 	PG_RETURN_POINTER(nrm.buf);
 }
+
+/*
+ * ltxtquery type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the input function, but it's prefixed with a version number so we
+ * can change the binary format sent in future if necessary. For now,
+ * only version 1 is supported.
+ */
+PG_FUNCTION_INFO_V1(ltxtq_send);
+Datum
+ltxtq_send(PG_FUNCTION_ARGS)
+{
+	ltxtquery  *query = PG_GETARG_LTXTQUERY_P(0);
+	StringInfoData buf;
+	int			version = 1;
+	INFIX		nrm;
+
+	if (query->size == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("syntax error"),
+				 errdetail("Empty query.")));
+
+	nrm.curpol = GETQUERY(query);
+	nrm.buflen = 32;
+	nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
+	*(nrm.cur) = '\0';
+	nrm.op = GETOPERAND(query);
+	infix(&nrm, true);
+
+	PG_FREE_IF_COPY(query, 0);
+	pq_begintypsend(&buf);
+	pq_sendint8(&buf, version);
+	pq_sendtext(&buf, nrm.buf, pg_mbstrlen(nrm.buf));
+	pfree(nrm.buf);
+
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
-- 
2.23.0

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Nino Floris (#9)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

Nino Floris <mail@ninofloris.com> writes:

Attached is the new patch rebased onto master.

The approach of simply providing a 1.2 installation script, with no
upgrade path from 1.1, is surely not going to be acceptable to anyone
who's using ltree in production.

Fortunately for the odds of getting this patch accepted, we just
pushed an ALTER TYPE improvement that will solve your problem [1]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=fe30e7ebfa3846416f1adeb7cf611006513a4ee0.
Please replace ltree--1.2.sql with an upgrade script that uses
that, and resubmit.

regards, tom lane

[1]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=fe30e7ebfa3846416f1adeb7cf611006513a4ee0

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#10)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

I wrote:

Fortunately for the odds of getting this patch accepted, we just
pushed an ALTER TYPE improvement that will solve your problem [1].
Please replace ltree--1.2.sql with an upgrade script that uses
that, and resubmit.

I decided it would be a shame for this to miss v13, seeing that
(a) it'd provide real-world use of that ALTER TYPE code, and
(b) we already bumped ltree's extension version for v13.

So I went ahead and rebased this, reviewed and pushed it. The
main non-cosmetic thing I changed, other than using ALTER TYPE,
was that you had the output functions looking like this:

pq_sendtext(&buf, res, pg_mbstrlen(res));

That's just wrong; it should be plain strlen(). Did you copy
that coding from someplace? I could not find any similar
occurrences in our code tree.

regards, tom lane

#12Nino Floris
mail@ninofloris.com
In reply to: Nino Floris (#3)
Re: [PATCH] ltree, lquery, and ltxtquery binary protocol support

Hi Tom,

Thanks a lot for pushing this through.

In complete agreement on fixing mbstrlen, it would clearly have lead to cut
off string sends, or worse (does the binary protocol use null terminated
strings, or are they length prefixed?). Apologies anyways, it's been a
while so I don't know how it may have ended up there, thanks for catching
it.

Even though it was a bit bumpy, and vastly different from my usual github
contribution flow, I've had a good time contributing my first patch!

Best regards,
Nino Floris

On Apr 1, 2020 at 23:37, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I wrote:

Fortunately for the odds of getting this patch accepted, we just
pushed an ALTER TYPE improvement that will solve your problem [1].
Please replace ltree--1.2.sql with an upgrade script that uses
that, and resubmit.

I decided it would be a shame for this to miss v13, seeing that
(a) it'd provide real-world use of that ALTER TYPE code, and
(b) we already bumped ltree's extension version for v13.

So I went ahead and rebased this, reviewed and pushed it. The
main non-cosmetic thing I changed, other than using ALTER TYPE,
was that you had the output functions looking like this:

pq_sendtext(&buf, res, pg_mbstrlen(res));

That's just wrong; it should be plain strlen(). Did you copy
that coding from someplace? I could not find any similar
occurrences in our code tree.

regards, tom lane