2016-11-22 21:47 GMT+01:00 Alvaro Herrera <alvhe...@2ndquadrant.com>:
> I found the whole TableExprGetTupleDesc() function a bit odd in > nodeFuncs.c, so I renamed it to ExecTypeFromTableExpr() and moved it to > execTuples.c -- but only because that's where ExecTypeFromTL and others > already live. I would have liked to move it to tupdesc.c instead, but > it requires knowledge of executor nodes, which is probably the reason > that ExecTypeFromTL is in execTuples. I think we'd eat that bit of > ugliness only because we're not the first. But anyway I quickly ran > into another problem. > > I noticed that ExecTypeFromTableExpr is being called from the transform > phase, which is much earlier than the executor. I noticed because of > the warning that the above movement added to nodeFuncs.c, > src/backend/nodes/nodeFuncs.c:509:5: warning: implicit declaration of > function 'ExecTypeFromTableExpr' [-Wimplicit-function-declaration] > The tuple descriptor should not be serialized. When xmltable is called directly, then living tuple descriptor is used - created in transform time. Another situation is when xmltable is used from view, where transform time is skipped. Originally I serialized generated type - but I had the problems with record types - the current infrastructure expects serialization only real types. My solution is a recheck of tuple descriptor in executor time. It is small overhead - once per query - but it allows use xmltable from views without necessity to specify returned columns explicitly. > > so I thought, hm, is it okay to have parse analysis run an executor > function? (I suppose this is the reason you put it in nodeFuncs in the > first place). For fun, I tried this query under GDB, with a breakpoint > on exprTypmod(): > > SELECT X.* > FROM emp, > XMLTABLE ('//depts/dept/employee' passing doc > COLUMNS > empID INTEGER PATH '@id', > firstname int PATH 'name/first', > lastname VARCHAR(25) PATH 'name/last') AS X; > > and sure enough, the type is resolved during parse analysis: > > Breakpoint 1, exprTypmod (expr=expr@entry=0x1d23ad8) > at /pgsql/source/master/src/backend/nodes/nodeFuncs.c:283 > 283 if (!expr) > (gdb) print *expr > $2 = {type = T_TableExpr} > (gdb) bt > #0 exprTypmod (expr=expr@entry=0x1d23ad8) > at /pgsql/source/master/src/backend/nodes/nodeFuncs.c:283 > #1 0x000000000080c500 in get_expr_result_type (expr=0x1d23ad8, > resultTypeId=0x7ffd482bfdb4, resultTupleDesc=0x7ffd482bfdb8) > at /pgsql/source/master/src/backend/utils/fmgr/funcapi.c:247 > #2 0x000000000056de1b in expandRTE (rte=rte@entry=0x1d6b800, rtindex=2, > sublevels_up=0, location=location@entry=7, > include_dropped=include_dropped@entry=0 '\000', > colnames=colnames@entry=0x7ffd482bfe10, colvars=0x7ffd482bfe18) > at /pgsql/source/master/src/backend/parser/parse_relation.c:2052 > #3 0x000000000056e131 in expandRelAttrs (pstate=pstate@entry=0x1d238a8, > rte=rte@entry=0x1d6b800, rtindex=<optimized out>, > sublevels_up=<optimized out>, location=location@entry=7) > at /pgsql/source/master/src/backend/parser/parse_relation.c:2435 > #4 0x000000000056fa64 in ExpandSingleTable (pstate=pstate@entry= > 0x1d238a8, > rte=rte@entry=0x1d6b800, location=7, > make_target_entry=make_target_entry@entry=1 '\001') > at /pgsql/source/master/src/backend/parser/parse_target.c:1266 > #5 0x000000000057135b in ExpandColumnRefStar (pstate=pstate@entry= > 0x1d238a8, > cref=0x1d22720, make_target_entry=make_target_entry@entry=1 '\001') > at /pgsql/source/master/src/backend/parser/parse_target.c:1158 > #6 0x00000000005716f9 in transformTargetList (pstate=0x1d238a8, > targetlist=<optimized out>, exprKind=EXPR_KIND_SELECT_TARGET) > > This seems fine I guess, and it seems to say that we ought to move the > code that generates the tupdesc to back parse analysis rather than > executor. Okay, fine. But let's find a better place than nodeFuncs. > > But if I move the XMLTABLE() call to the target list instead, the type > is resolved at planner time: > > SELECT > XMLTABLE ('/dept/employee' passing $$<dept bldg="114"> > <employee id="903"> > <name> > <first>Mary</first> > <last>Jones</last> > </name> > <office>415</office> > <phone>905-403-6112</phone> > <phone>647-504-4546</phone> > <salary currency="USD">64000</salary> > </employee> > </dept>$$ > COLUMNS > empID INTEGER PATH '@id', > firstname varchar(4) PATH 'name/first', > lastname VARCHAR(25) PATH 'name/last') AS X; > > Breakpoint 1, exprTypmod (expr=expr@entry=0x1d6bed8) > at /pgsql/source/master/src/backend/nodes/nodeFuncs.c:283 > 283 if (!expr) > (gdb) bt > #0 exprTypmod (expr=expr@entry=0x1d6bed8) > at /pgsql/source/master/src/backend/nodes/nodeFuncs.c:283 > #1 0x0000000000654058 in set_pathtarget_cost_width (root=0x1d6bc68, > target=0x1d6c728) > at /pgsql/source/master/src/backend/optimizer/path/costsize.c:4729 > #2 0x000000000066c197 in grouping_planner (root=0x1d6bc68, > inheritance_update=40 '(', inheritance_update@entry=0 '\000', > tuple_fraction=0.01, tuple_fraction@entry=0) > at /pgsql/source/master/src/backend/optimizer/plan/planner.c:1745 > #3 0x000000000066ef64 in subquery_planner (glob=glob@entry=0x1d6bbd0, > parse=parse@entry=0x1d23818, parent_root=parent_root@entry=0x0, > hasRecursion=hasRecursion@entry=0 '\000', > tuple_fraction=tuple_fraction@entry=0) > at /pgsql/source/master/src/backend/optimizer/plan/planner.c:795 > #4 0x000000000066fe5e in standard_planner (parse=0x1d23818, > cursorOptions=256, boundParams=<optimized out>) > at /pgsql/source/master/src/backend/optimizer/plan/planner.c:307 > > This is surprising, but I'm not sure it's wrong. > There are different processing for Set Returning nodes called from paramlist and from tablelist. In last case the invokes exprTypmod early. There is a different case, that you didn't check CREATE VIEW x AS SELECT xmltable(...) CREATE VIEW x1 AS SELECT * FROM xmltable(...) close session and in new session SELECT * FROM x; SELECT * FROM x1; Regards Pavel > > -- > Álvaro Herrera https://www.2ndQuadrant.com/ > PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services >