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

