X-Git-Url: http://www.sigaev.ru/git/gitweb.cgi?p=hstore.git;a=blobdiff_plain;f=hstore_gram.y;fp=hstore_gram.y;h=8db2f60122f2d21fbb5a8e4a6daeeb18b00f1a15;hp=0000000000000000000000000000000000000000;hb=2d3cb5062568eab105ed554350ac99bae76ee0ec;hpb=77af220c462dd61507d6cca9b9f54ad3e102e1b6 diff --git a/hstore_gram.y b/hstore_gram.y new file mode 100644 index 0000000..8db2f60 --- /dev/null +++ b/hstore_gram.y @@ -0,0 +1,315 @@ +%{ +#define YYPARSE_PARAM result /* need this to pass a pointer (void *) to yyparse */ + +#include "postgres.h" + +#include "fmgr.h" +#include "utils/builtins.h" +#include "hstore.h" + +/* + * Bison doesn't allocate anything that needs to live across parser calls, + * so we can easily have it use palloc instead of malloc. This prevents + * memory leaks if we error out during parsing. Note this only works with + * bison >= 2.0. However, in bison 1.875 the default is to use alloca() + * if possible, so there's not really much problem anyhow, at least if + * you're building with gcc. + */ +#define YYMALLOC palloc +#define YYFREE pfree + +/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ +#undef fprintf +#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg) + +static void +fprintf_to_ereport(const char *fmt, const char *msg) +{ + ereport(ERROR, (errmsg_internal("%s", msg))); +} + +/* struct string is shared between scan and gram */ +typedef struct string { + char *val; + int len; + int total; +} string; +#include + +/* flex 2.5.4 doesn't bother with a decl for this */ +int hstore_yylex(YYSTYPE * yylval_param); +int hstore_yyparse(void *result); +void hstore_yyerror(const char *message); + +static HStoreValue* +makeHStoreValueString(HStoreValue* v, string *s) +{ + if (v == NULL) + v = palloc(sizeof(*v)); + + if (s == NULL) + { + v->type = hsvNull; + v->size = sizeof(HEntry); + } + else if (s->len > HENTRY_POSMASK) + { + elog(ERROR, "string is too long"); + } + else + { + v->type = hsvString; + v->string.val = s->val; + v->string.len = s->len; + v->size = sizeof(HEntry) + s->len; + + } + + return v; +} + +static HStoreValue* +makeHStoreValueNumeric(string *s) +{ + Numeric n = NULL; + HStoreValue *v; + MemoryContext ccxt = CurrentMemoryContext; + + /* + * ignore ERRCODE_INVALID_TEXT_REPRESENTATION in parse: our + * test stringIsNumber could be not agree with numeric_in + */ + + PG_TRY(); + { + n = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(s->val), 0, -1)); + } + PG_CATCH(); + { + ErrorData *errdata; + MemoryContext ecxt; + + ecxt = MemoryContextSwitchTo(ccxt); + errdata = CopyErrorData(); + if (errdata->sqlerrcode == ERRCODE_INVALID_TEXT_REPRESENTATION) + { + FlushErrorState(); + n = NULL; + } + else + { + MemoryContextSwitchTo(ecxt); + PG_RE_THROW(); + } + } + PG_END_TRY(); + + if (n != NULL) + { + v = palloc(sizeof(*v)); + v->type = hsvNumeric; + v->numeric = n; + v->size = 2*sizeof(HEntry) + VARSIZE_ANY(n); + } + else + { + v = makeHStoreValueString(NULL, s); + } + + return v; +} + +static HStoreValue* +makeHStoreValueBool(bool val) { + HStoreValue *v = palloc(sizeof(*v)); + + v->type = hsvBool; + v->boolean = val; + v->size = sizeof(HEntry); + + return v; +} + +static HStoreValue* +makeHStoreValueArray(List *list) +{ + HStoreValue *v = palloc(sizeof(*v)); + + v->type = hsvArray; + v->array.scalar = false; + v->array.nelems = list_length(list); + v->size = sizeof(uint32) /* header */ + sizeof(HEntry) /* parent's entry */ + sizeof(HEntry) - 1 /*alignment*/; + + if (v->array.nelems > 0) + { + ListCell *cell; + int i = 0; + + v->array.elems = palloc(sizeof(HStoreValue) * v->array.nelems); + + foreach(cell, list) + { + HStoreValue *s = (HStoreValue*)lfirst(cell); + + v->size += s->size; + + v->array.elems[i++] = *s; + + if (v->size > HENTRY_POSMASK) + elog(ERROR, "array is too long"); + } + } + else + { + v->array.elems = NULL; + } + + return v; +} + +static HStoreValue* +makeHStoreValuePairs(List *list) +{ + HStoreValue *v = palloc(sizeof(*v)); + + v->type = hsvHash; + v->hash.npairs = list_length(list); + v->size = sizeof(uint32) /* header */ + sizeof(HEntry) /* parent's entry */ + sizeof(HEntry) - 1 /*alignment*/; + + if (v->hash.npairs > 0) + { + ListCell *cell; + int i = 0; + + v->hash.pairs = palloc(sizeof(HStorePair) * v->hash.npairs); + + foreach(cell, list) + { + HStorePair *s = (HStorePair*)lfirst(cell); + + v->size += s->key.size + s->value.size; + v->hash.pairs[i].order = i; + v->hash.pairs[i++] = *s; + + if (v->size > HENTRY_POSMASK) + elog(ERROR, "hstore is too long"); + } + + uniqueHStoreValue(v); + } + else + { + v->hash.pairs = NULL; + } + + return v; +} + +static HStorePair* +makeHStorePair(string *key, HStoreValue *value) { + HStorePair *v = palloc(sizeof(*v)); + + makeHStoreValueString(&v->key, key); + v->value = *value; + + return v; +} + +%} + +/* BISON Declarations */ +%pure-parser +%expect 0 +%name-prefix="hstore_yy" +%error-verbose + +%union { + string str; + Numeric numeric; + List *elems; /* list of HStoreValue */ + List *pairs; /* list of HStorePair */ + + HStoreValue *hvalue; + HStorePair *pair; +} + +%token DELIMITER_P NULL_P STRING_P TRUE_P FALSE_P + NUMERIC_P + +%type result hstore value scalar_value +%type key + +%type pair + +%type value_list +%type pair_list + +/* Grammar follows */ +%% + +result: + pair_list { *((HStoreValue**)result) = makeHStoreValuePairs($1); } + | hstore { + if ($1->type == hsvNull) + *((HStoreValue**)result) = NULL; + else + *((HStoreValue**)result) = $1; + } + | scalar_value { + *((HStoreValue**)result) = makeHStoreValueArray(lappend(NIL, $1)); + (*((HStoreValue**)result))->array.scalar = true; + } + | /* EMPTY */ { *((HStoreValue**)result) = NULL; } + ; + +hstore: + '{' pair_list '}' { $$ = makeHStoreValuePairs($2); } + | '{' value_list '}' { $$ = makeHStoreValueArray($2); } + | '[' value_list ']' { $$ = makeHStoreValueArray($2); } + | '{' value '}' { $$ = makeHStoreValueArray(lappend(NIL, $2)); } + | '[' value ']' { $$ = makeHStoreValueArray(lappend(NIL, $2)); } + | '{' '}' { $$ = makeHStoreValueString(NULL, NULL); } + | '[' ']' { $$ = makeHStoreValueString(NULL, NULL); } + ; + +scalar_value: + NULL_P { $$ = makeHStoreValueString(NULL, NULL); } + | STRING_P { $$ = makeHStoreValueString(NULL, &$1); } + | TRUE_P { $$ = makeHStoreValueBool(true); } + | FALSE_P { $$ = makeHStoreValueBool(false); } + | NUMERIC_P { $$ = makeHStoreValueNumeric(&$1); } + ; + +value: + scalar_value { $$ = $1; } + | hstore { $$ = $1; } + ; + +value_list: + value ',' value { $$ = lappend(lappend(NIL, $1), $3); } + | value_list ',' value { $$ = lappend($1, $3); } + ; + +/* + * key is always a string, not a bool or numeric + */ +key: + STRING_P { $$ = $1; } + | TRUE_P { $$ = $1; } + | FALSE_P { $$ = $1; } + | NUMERIC_P { $$ = $1; } + | NULL_P { $$ = $1; } + ; + +pair: + key DELIMITER_P value { $$ = makeHStorePair(&$1, $3); } + ; + +pair_list: + pair { $$ = lappend(NIL, $1); } + | pair_list ',' pair { $$ = lappend($1, $3); } + ; + +%% + +#include "hstore_scan.c"