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 <[email protected]>:
>
>
> 2014-10-22 18:35 GMT+02:00 Merlin Moncure <[email protected]>:
>
>> On Wed, Oct 22, 2014 at 11:21 AM, Pavel Stehule <[email protected]>
>> 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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers