Commit 949a9f04 authored by Tom Lane's avatar Tom Lane

Add support for binary I/O of ltree, lquery, and ltxtquery types.

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 a8081860,
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
parent 501b0187
...@@ -20,10 +20,10 @@ ...@@ -20,10 +20,10 @@
#include "utils/pg_crc.h" #include "utils/pg_crc.h"
unsigned int unsigned int
ltree_crc32_sz(char *buf, int size) ltree_crc32_sz(const char *buf, int size)
{ {
pg_crc32 crc; pg_crc32 crc;
char *p = buf; const char *p = buf;
INIT_TRADITIONAL_CRC32(crc); INIT_TRADITIONAL_CRC32(crc);
while (size > 0) while (size > 0)
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
/* contrib/ltree/crc32.h */ /* contrib/ltree/crc32.h */
/* Returns crc32 of data block */ /* 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 */ /* Returns crc32 of null-terminated string */
#define crc32(buf) ltree_crc32_sz((buf),strlen(buf)) #define crc32(buf) ltree_crc32_sz((buf),strlen(buf))
......
...@@ -3,6 +3,43 @@ ...@@ -3,6 +3,43 @@
-- complain if script is sourced in psql, rather than via ALTER EXTENSION -- 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 \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) CREATE FUNCTION ltree_gist_options(internal)
RETURNS void RETURNS void
AS 'MODULE_PATHNAME', 'ltree_gist_options' AS 'MODULE_PATHNAME', 'ltree_gist_options'
......
...@@ -8,18 +8,14 @@ ...@@ -8,18 +8,14 @@
#include <ctype.h> #include <ctype.h>
#include "crc32.h" #include "crc32.h"
#include "libpq/pqformat.h"
#include "ltree.h" #include "ltree.h"
#include "utils/memutils.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 typedef struct
{ {
char *start; const char *start;
int len; /* length in bytes */ int len; /* length in bytes */
int flag; int flag;
int wlen; /* length in characters */ int wlen; /* length in characters */
...@@ -28,11 +24,14 @@ typedef struct ...@@ -28,11 +24,14 @@ typedef struct
#define LTPRS_WAITNAME 0 #define LTPRS_WAITNAME 0
#define LTPRS_WAITDELIM 1 #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); const char *ptr;
char *ptr;
nodeitem *list, nodeitem *list,
*lptr; *lptr;
int num = 0, int num = 0,
...@@ -141,15 +140,18 @@ ltree_in(PG_FUNCTION_ARGS) ...@@ -141,15 +140,18 @@ ltree_in(PG_FUNCTION_ARGS)
} }
pfree(list); pfree(list);
PG_RETURN_POINTER(result); return result;
#undef UNCHAR #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, char *buf,
*ptr; *ptr;
int i; int i;
...@@ -170,11 +172,84 @@ ltree_out(PG_FUNCTION_ARGS) ...@@ -170,11 +172,84 @@ ltree_out(PG_FUNCTION_ARGS)
} }
*ptr = '\0'; *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_WAITLEVEL 0
#define LQPRS_WAITDELIM 1 #define LQPRS_WAITDELIM 1
#define LQPRS_WAITOPEN 2 #define LQPRS_WAITOPEN 2
...@@ -190,11 +265,14 @@ ltree_out(PG_FUNCTION_ARGS) ...@@ -190,11 +265,14 @@ ltree_out(PG_FUNCTION_ARGS)
#define ITEMSIZE MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*)) #define ITEMSIZE MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
#define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) ) #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); const char *ptr;
char *ptr;
int num = 0, int num = 0,
totallen = 0, totallen = 0,
numOR = 0; numOR = 0;
...@@ -563,15 +641,18 @@ lquery_in(PG_FUNCTION_ARGS) ...@@ -563,15 +641,18 @@ lquery_in(PG_FUNCTION_ARGS)
} }
pfree(tmpql); pfree(tmpql);
PG_RETURN_POINTER(result); return result;
#undef UNCHAR #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, char *buf,
*ptr; *ptr;
int i, int i,
...@@ -679,7 +760,79 @@ lquery_out(PG_FUNCTION_ARGS) ...@@ -679,7 +760,79 @@ lquery_out(PG_FUNCTION_ARGS)
} }
*ptr = '\0'; *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);
} }
...@@ -8,12 +8,10 @@ ...@@ -8,12 +8,10 @@
#include <ctype.h> #include <ctype.h>
#include "crc32.h" #include "crc32.h"
#include "libpq/pqformat.h"
#include "ltree.h" #include "ltree.h"
#include "miscadmin.h" #include "miscadmin.h"
PG_FUNCTION_INFO_V1(ltxtq_in);
PG_FUNCTION_INFO_V1(ltxtq_out);
/* parser's states */ /* parser's states */
#define WAITOPERAND 1 #define WAITOPERAND 1
...@@ -381,12 +379,41 @@ queryin(char *buf) ...@@ -381,12 +379,41 @@ queryin(char *buf)
/* /*
* in without morphology * in without morphology
*/ */
PG_FUNCTION_INFO_V1(ltxtq_in);
Datum Datum
ltxtq_in(PG_FUNCTION_ARGS) ltxtq_in(PG_FUNCTION_ARGS)
{ {
PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0))); 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 * out function
*/ */
...@@ -511,6 +538,7 @@ infix(INFIX *in, bool first) ...@@ -511,6 +538,7 @@ infix(INFIX *in, bool first)
} }
} }
PG_FUNCTION_INFO_V1(ltxtq_out);
Datum Datum
ltxtq_out(PG_FUNCTION_ARGS) ltxtq_out(PG_FUNCTION_ARGS)
{ {
...@@ -530,6 +558,43 @@ ltxtq_out(PG_FUNCTION_ARGS) ...@@ -530,6 +558,43 @@ ltxtq_out(PG_FUNCTION_ARGS)
nrm.op = GETOPERAND(query); nrm.op = GETOPERAND(query);
infix(&nrm, true); infix(&nrm, true);
PG_FREE_IF_COPY(query, 0);
PG_RETURN_POINTER(nrm.buf); 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));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment