mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-19 20:00:51 +08:00
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 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
This commit is contained in:
parent
501b018799
commit
949a9f043e
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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'
|
||||
|
@ -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);
|
||||
|
||||
PG_RETURN_POINTER(buf);
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
PG_RETURN_POINTER(buf);
|
||||
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(res);
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user