mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-22 04:33:22 +02:00
By project convention, these names should include "P" when dealing with a pointer type; that is, if the result of a GETARG macro is of type FOO *, it should be called PG_GETARG_FOO_P not just PG_GETARG_FOO. Some newer types such as JSONB and ranges had not followed the convention, and a number of contrib modules hadn't gotten that memo either. Rename the offending macros to improve consistency. In passing, fix a few places that thought PG_DETOAST_DATUM() returns a Datum; it does not, it returns "struct varlena *". Applying DatumGetPointer to that happens not to cause any bad effects today, but it's formally wrong. Also, adjust an ltree macro that was designed without any thought for what pgindent would do with it. This is all cosmetic and shouldn't have any impact on generated code. Mark Dilger, some further tweaks by me Discussion: https://postgr.es/m/EA5676F4-766F-4F38-8348-ECC7DB427C6A@gmail.com
621 lines
13 KiB
C
621 lines
13 KiB
C
/*
|
|
* in/out function for ltree and lquery
|
|
* Teodor Sigaev <teodor@stack.net>
|
|
* contrib/ltree/ltree_io.c
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "ltree.h"
|
|
#include "utils/memutils.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;
|
|
int len; /* length in bytes */
|
|
int flag;
|
|
int wlen; /* length in characters */
|
|
} nodeitem;
|
|
|
|
#define LTPRS_WAITNAME 0
|
|
#define LTPRS_WAITDELIM 1
|
|
|
|
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);
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
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';
|
|
PG_FREE_IF_COPY(in, 0);
|
|
|
|
PG_RETURN_POINTER(buf);
|
|
}
|
|
|
|
#define LQPRS_WAITLEVEL 0
|
|
#define LQPRS_WAITDELIM 1
|
|
#define LQPRS_WAITOPEN 2
|
|
#define LQPRS_WAITFNUM 3
|
|
#define LQPRS_WAITSNUM 4
|
|
#define LQPRS_WAITND 5
|
|
#define LQPRS_WAITCLOSE 6
|
|
#define LQPRS_WAITEND 7
|
|
#define LQPRS_WAITVAR 8
|
|
|
|
|
|
#define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) )
|
|
#define ITEMSIZE MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
|
|
#define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) )
|
|
|
|
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);
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
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);
|
|
|
|
PG_RETURN_POINTER(buf);
|
|
}
|