mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-29 07:07:18 +02:00
Because of gcc -Wmissing-prototypes, all functions in dynamically loadable modules must have a separate prototype declaration. This is meant to detect global functions that are not declared in header files, but in cases where the function is called via dfmgr, this is redundant. Besides filling up space with boilerplate, this is a frequent source of compiler warnings in extension modules. We can fix that by creating the function prototype as part of the PG_FUNCTION_INFO_V1 macro, which such modules have to use anyway. That makes the code of modules cleaner, because there is one less place where the entry points have to be listed, and creates an additional check that functions have the right prototype. Remove now redundant prototypes from contrib and other modules.
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);
|
|
}
|