Add support for binary I/O of ltree, lquery, and ltxtquery types.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 1 Apr 2020 21:31:29 +0000 (17:31 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 1 Apr 2020 21:31:29 +0000 (17:31 -0400)
Not much to say here --- does what it says on the tin.  The "binary"
representation in each case is really just the same as the text format,
though we prefix a version-number byte in case anyone ever feels
motivated to change that.  Thus, there's not any expectation of improved
speed or reduced space; the point here is just to allow clients to use
binary format for all columns of a query result or COPY data.

This makes use of the recently added ALTER TYPE support to add binary
I/O functions to an existing data type.  As in commit a80818605,
we can piggy-back on there already being a new-for-v13 version of the
ltree extension, so we don't need a new update script file.

Nino Floris, reviewed by Alexander Korotkov and myself

Discussion: https://postgr.es/m/CANmj9Vxx50jOo1L7iSRxd142NyTz6Bdcgg7u9P3Z8o0=HGkYyQ@mail.gmail.com

contrib/ltree/crc32.c
contrib/ltree/crc32.h
contrib/ltree/ltree--1.1--1.2.sql
contrib/ltree/ltree_io.c
contrib/ltree/ltxtquery_io.c

index 0c3e45923b2e4f6d7b2fd5bd85a5d9912e9341e4..8fed3346e8a005780497844a6b58edeb847d5087 100644 (file)
 #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)
index 269d05d0c11b87870f38abddd51fcfdeef6bd066..958812214df7da063d4a41f45ead9fbd0ea6f424 100644 (file)
@@ -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))
index 186381e61d87a78339cc2a32696805c558cf7567..e38e76b31e2defa6a26ca588c7043745d31cd574 100644 (file)
@@ -3,6 +3,43 @@
 -- complain if script is sourced in psql, rather than via ALTER EXTENSION
 \echo Use "ALTER EXTENSION ltree UPDATE TO '1.2'" to load this file. \quit
 
+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;
+
+ALTER TYPE ltree SET ( RECEIVE = ltree_recv, SEND = ltree_send );
+
+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;
+
+ALTER TYPE lquery SET ( RECEIVE = lquery_recv, SEND = lquery_send );
+
+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;
+
+ALTER TYPE ltxtquery SET ( RECEIVE = ltxtq_recv, SEND = ltxtq_send );
+
+
 CREATE FUNCTION ltree_gist_options(internal)
 RETURNS void
 AS 'MODULE_PATHNAME', 'ltree_gist_options'
index c6ea5dec8c94edaeb53c664eb02f172f3fc620e8..b928b82d624080bce7b2d0e099e71545320161cd 100644 (file)
@@ -8,18 +8,14 @@
 #include <ctype.h>
 
 #include "crc32.h"
+#include "libpq/pqformat.h"
 #include "ltree.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);
-
 
 typedef struct
 {
-       char       *start;
+       const char *start;
        int                     len;                    /* length in bytes */
        int                     flag;
        int                     wlen;                   /* length in characters */
@@ -28,11 +24,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,
@@ -141,15 +140,18 @@ ltree_in(PG_FUNCTION_ARGS)
        }
 
        pfree(list);
-       PG_RETURN_POINTER(result);
+       return result;
 
 #undef UNCHAR
 }
 
-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 +172,84 @@ ltree_out(PG_FUNCTION_ARGS)
        }
 
        *ptr = '\0';
-       PG_FREE_IF_COPY(in, 0);
+       return buf;
+}
+
+/*
+ * Basic ltree I/O functions
+ */
+PG_FUNCTION_INFO_V1(ltree_in);
+Datum
+ltree_in(PG_FUNCTION_ARGS)
+{
+       char       *buf = (char *) PG_GETARG_POINTER(0);
+
+       PG_RETURN_POINTER(parse_ltree(buf));
+}
+
+PG_FUNCTION_INFO_V1(ltree_out);
+Datum
+ltree_out(PG_FUNCTION_ARGS)
+{
+       ltree      *in = PG_GETARG_LTREE_P(0);
+
+       PG_RETURN_POINTER(deparse_ltree(in));
+}
+
+/*
+ * ltree type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the output 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, strlen(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;
 
-       PG_RETURN_POINTER(buf);
+       if (version != 1)
+               elog(ERROR, "unsupported ltree version number %d", version);
+
+       str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+       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;
@@ -563,15 +641,18 @@ lquery_in(PG_FUNCTION_ARGS)
        }
 
        pfree(tmpql);
-       PG_RETURN_POINTER(result);
+       return result;
 
 #undef UNCHAR
 }
 
-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,
@@ -679,7 +760,79 @@ lquery_out(PG_FUNCTION_ARGS)
        }
 
        *ptr = '\0';
-       PG_FREE_IF_COPY(in, 0);
+       return buf;
+}
+
+/*
+ * Basic lquery I/O functions
+ */
+PG_FUNCTION_INFO_V1(lquery_in);
+Datum
+lquery_in(PG_FUNCTION_ARGS)
+{
+       char       *buf = (char *) PG_GETARG_POINTER(0);
+
+       PG_RETURN_POINTER(parse_lquery(buf));
+}
+
+PG_FUNCTION_INFO_V1(lquery_out);
+Datum
+lquery_out(PG_FUNCTION_ARGS)
+{
+       lquery     *in = PG_GETARG_LQUERY_P(0);
+
+       PG_RETURN_POINTER(deparse_lquery(in));
+}
+
+/*
+ * lquery type send function
+ *
+ * The type is sent as text in binary mode, so this is almost the same
+ * as the output 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, strlen(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)
+               elog(ERROR, "unsupported lquery version number %d", version);
+
+       str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+       res = parse_lquery(str);
+       pfree(str);
 
-       PG_RETURN_POINTER(buf);
+       PG_RETURN_POINTER(res);
 }
index db347f77721e16b784207ecc081aa4db012f41da..d967f92110fed68b376885855be79f1ccaa7c363 100644 (file)
@@ -8,12 +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
@@ -381,12 +379,41 @@ 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)
+               elog(ERROR, "unsupported ltxtquery version number %d", version);
+
+       str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+       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)
 {
@@ -530,6 +558,43 @@ ltxtq_out(PG_FUNCTION_ARGS)
        nrm.op = GETOPERAND(query);
        infix(&nrm, true);
 
-       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 output 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);
+
+       pq_begintypsend(&buf);
+       pq_sendint8(&buf, version);
+       pq_sendtext(&buf, nrm.buf, strlen(nrm.buf));
+       pfree(nrm.buf);
+
+       PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}