Hi here is a prototype
postgres=# select row_to_json(row(10 as A, row(30 as c, 20 AS B) as x)); row_to_json ------------------------------ {"a":10,"x":{"c":30,"b":20}} (1 row) postgres=# select row_to_json(row(10, row(30, 20))); row_to_json ---------------------------------- {"f1":10,"f2":{"f1":30,"f2":20}} (1 row) Regards Pavel 2014-10-22 19:09 GMT+02:00 Pavel Stehule <pavel.steh...@gmail.com>: > > > 2014-10-22 18:35 GMT+02:00 Merlin Moncure <mmonc...@gmail.com>: > >> On Wed, Oct 22, 2014 at 11:21 AM, Pavel Stehule <pavel.steh...@gmail.com> >> wrote: >> > Hi >> > >> > with new functions row_to_json(b), there is more often usage of ROW >> > constructor. Using names in fields is relative difficult. Because ROW >> has >> > special clause in parser, I am thinking so we can enable labeling >> inside ROW >> > constructor >> > >> > so instead currently supported: >> > >> > select row_to_json(r) from (select 10 as a, 20 as b) r; >> > >> > users can to write: >> > >> > select row_to_json(row(10 as a,20 as b)); >> > >> > labeling will be enabled "only" inside ROW constructor. I don't propose >> > enable it everywhere. >> > >> > What do you think about it? >> >> It's a neat idea -- maybe a better alternative to what I was thinking >> here: >> http://postgresql.1045698.n5.nabble.com/Support-UPDATE-table-SET-tp5823073p5823944.html >> >> Some questions: >> *) What would the parser transformation resolve to >> > > row: ROW '(' expr_list ')' { $$ > = $3; } > | ROW '(' > ')' { $$ = NIL; } > | '(' expr_list ',' a_expr ')' { > $$ = lappend($2, $4); } > ; > > we can replace a expr_list by target_list. I know only so it doesn't > enforce a problems with gramatic - bison doesn't raise any warning. > > > *) Are we ok with SQL standard >> > > SQL standard doesn't think named attributes in row - so it is out of range > ANSI. But it is not in conflict with standard. "AS name" is used more in > SQL/MM, SQL/XML -- and function named parameters has different syntax > "parameter_name <= value" - I checked it against SQL99. > > >> *) Do you think this (or some similar variant) would work? >> >> select row_to_json(row(foo.*)) from foo; >> > > It looks like independent feature and can work too - it is more natural > for user. > > >> >> merlin >> > >
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y new file mode 100644 index 0de9584..682506d *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** static void processCASbits(int cas_bits, *** 174,179 **** --- 174,180 ---- bool *deferrable, bool *initdeferred, bool *not_valid, bool *no_inherit, core_yyscan_t yyscanner); static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); + static List *extractArgs(List *target_list); %} *************** static Node *makeRecursiveViewSelect(cha *** 431,437 **** %type <list> ExclusionConstraintList ExclusionConstraintElem %type <list> func_arg_list %type <node> func_arg_expr ! %type <list> row type_list array_expr_list %type <node> case_expr case_arg when_clause case_default %type <list> when_clause_list %type <ival> sub_type --- 432,439 ---- %type <list> ExclusionConstraintList ExclusionConstraintElem %type <list> func_arg_list %type <node> func_arg_expr ! %type <list> row type_list array_expr_list row_field_list ! %type <target> row_field_el %type <node> case_expr case_arg when_clause case_default %type <list> when_clause_list %type <ival> sub_type *************** a_expr: c_expr { $$ = $1; } *** 11162,11179 **** } | row OVERLAPS row { ! if (list_length($1) != 2) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("wrong number of parameters on left side of OVERLAPS expression"), parser_errposition(@1))); ! if (list_length($3) != 2) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("wrong number of parameters on right side of OVERLAPS expression"), parser_errposition(@3))); $$ = (Node *) makeFuncCall(SystemFuncName("overlaps"), ! list_concat($1, $3), @2); } | a_expr IS TRUE_P %prec IS --- 11164,11184 ---- } | row OVERLAPS row { ! List *l1 = extractArgs($1); ! List *l2 = extractArgs($3); ! ! if (list_length(l1) != 2) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("wrong number of parameters on left side of OVERLAPS expression"), parser_errposition(@1))); ! if (list_length(l2) != 2) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("wrong number of parameters on right side of OVERLAPS expression"), parser_errposition(@3))); $$ = (Node *) makeFuncCall(SystemFuncName("overlaps"), ! list_concat(l1, l2), @2); } | a_expr IS TRUE_P %prec IS *************** frame_bound: *** 12302,12310 **** * without conflicting with the parenthesized a_expr production. Without the * ROW keyword, there must be more than one a_expr inside the parens. */ ! row: ROW '(' expr_list ')' { $$ = $3; } | ROW '(' ')' { $$ = NIL; } ! | '(' expr_list ',' a_expr ')' { $$ = lappend($2, $4); } ; sub_type: ANY { $$ = ANY_SUBLINK; } --- 12307,12338 ---- * without conflicting with the parenthesized a_expr production. Without the * ROW keyword, there must be more than one a_expr inside the parens. */ ! ! row: ROW '(' row_field_list ')' { $$ = $3; } | ROW '(' ')' { $$ = NIL; } ! | '(' row_field_list ',' row_field_el ')' { $$ = lappend($2, $4); } ! ; ! ! row_field_list: row_field_el { $$ = list_make1($1); } ! | row_field_list ',' row_field_el { $$ = lappend($1, $3); } ! ; ! ! row_field_el: a_expr AS ColLabel ! { ! $$ = makeNode(ResTarget); ! $$->name = $3; ! $$->indirection = NIL; ! $$->val = (Node *) $1; ! $$->location = @1; ! } ! | a_expr ! { ! $$ = makeNode(ResTarget); ! $$->name = NULL; ! $$->indirection = NIL; ! $$->val = (Node *) $1; ! $$->location = @1; ! } ; sub_type: ANY { $$ = ANY_SUBLINK; } *************** makeRecursiveViewSelect(char *relname, L *** 14223,14228 **** --- 14251,14279 ---- return (Node *) s; } + /* + * Separates args list from target list. It throw names, where we should + * not to use it. Returs expr_list from target_list. + */ + static List * + extractArgs(List *target_list) + { + ListCell *lc; + List *result = NIL; + + foreach(lc, target_list) + { + ResTarget *rt = (ResTarget *) lfirst(lc); + + Assert(IsA(rt, ResTarget)); + + /* some additional check should be here .... ToDo */ + result = lappend(result, rt->val); + } + + return result; + } + /* parser_init() * Initialize to parse one query string */ diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c new file mode 100644 index 4a8aaf6..751f9d8 *** a/src/backend/parser/parse_expr.c --- b/src/backend/parser/parse_expr.c *************** transformRowExpr(ParseState *pstate, Row *** 1869,1889 **** newr = makeNode(RowExpr); /* Transform the field expressions */ - newr->args = transformExpressionList(pstate, r->args, pstate->p_expr_kind); - - /* Barring later casting, we consider the type RECORD */ - newr->row_typeid = RECORDOID; - newr->row_format = COERCE_IMPLICIT_CAST; /* ROW() has anonymous columns, so invent some field names */ newr->colnames = NIL; fnum = 1; ! foreach(lc, newr->args) { ! snprintf(fname, sizeof(fname), "f%d", fnum++); ! newr->colnames = lappend(newr->colnames, makeString(pstrdup(fname))); } newr->location = r->location; return (Node *) newr; --- 1869,1906 ---- newr = makeNode(RowExpr); /* Transform the field expressions */ /* ROW() has anonymous columns, so invent some field names */ newr->colnames = NIL; + newr->args = NIL; fnum = 1; ! ! foreach(lc, r->args) { ! ResTarget *rt = (ResTarget *) lfirst(lc); ! Node *expr; ! Value *argname; ! ! Assert(IsA(rt, ResTarget)); ! ! expr = transformExprRecurse(pstate, rt->val); ! ! if (rt->name) ! argname = makeString(pstrdup(rt->name)); ! else ! { ! snprintf(fname, sizeof(fname), "f%d", fnum++); ! argname = makeString(pstrdup(fname)); ! } ! ! newr->colnames = lappend(newr->colnames, argname); ! newr->args = lappend(newr->args, expr); } + /* Barring later casting, we consider the type RECORD */ + newr->row_typeid = RECORDOID; + newr->row_format = COERCE_IMPLICIT_CAST; + newr->location = r->location; return (Node *) newr; *************** transformXmlExpr(ParseState *pstate, Xml *** 1988,1994 **** newx->location = x->location; /* ! * gram.y built the named args as a list of ResTarget. Transform each, * and break the names out as a separate list. */ newx->named_args = NIL; --- 2005,2011 ---- newx->location = x->location; /* ! * gram.y built the named args as a list of ResTarget. Transform each, * and break the names out as a separate list. */ newx->named_args = NIL;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers