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

