382ceffdf7
Don't move parenthesized lines to the left, even if that means they flow past the right margin. By default, BSD indent lines up statement continuation lines that are within parentheses so that they start just to the right of the preceding left parenthesis. However, traditionally, if that resulted in the continuation line extending to the right of the desired right margin, then indent would push it left just far enough to not overrun the margin, if it could do so without making the continuation line start to the left of the current statement indent. That makes for a weird mix of indentations unless one has been completely rigid about never violating the 80-column limit. This behavior has been pretty universally panned by Postgres developers. Hence, disable it with indent's new -lpl switch, so that parenthesized lines are always lined up with the preceding left paren. This patch is much less interesting than the first round of indent changes, but also bulkier, so I thought it best to separate the effects. Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
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(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(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);
|
|
}
|