From 8f43d2e60bf2a91bc326a50ceb0675ad7e476557 Mon Sep 17 00:00:00 2001 From: Nino Floris 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 #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 @@ -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