* drill down to find the ultimate defining expression and attempt to infer
* the tupdesc from it. We ereport if we can't determine the tupdesc.
*
- * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
+ * levelsup is an extra offset to interpret the Var's varlevelsup correctly
+ * when recursing. Outside callers should pass zero.
*/
TupleDesc
expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
/*
* Recurse into the sub-select to see what its Var refers
* to. We have to build an additional level of ParseState
- * to keep in step with varlevelsup in the subselect.
+ * to keep in step with varlevelsup in the subselect;
+ * furthermore, the subquery RTE might be from an outer
+ * query level, in which case the ParseState for the
+ * subselect must have that outer level as parent.
*/
ParseState mypstate = {0};
+ Index levelsup;
+ /* this loop must work, since GetRTEByRangeTablePosn did */
+ for (levelsup = 0; levelsup < netlevelsup; levelsup++)
+ pstate = pstate->parentParseState;
mypstate.parentParseState = pstate;
mypstate.p_rtable = rte->subquery->rtable;
/* don't bother filling the rest of the fake pstate */
* Recurse into the CTE to see what its Var refers to. We
* have to build an additional level of ParseState to keep
* in step with varlevelsup in the CTE; furthermore it
- * could be an outer CTE.
+ * could be an outer CTE (compare SUBQUERY case above).
*/
- ParseState mypstate;
+ ParseState mypstate = {0};
Index levelsup;
- MemSet(&mypstate, 0, sizeof(mypstate));
/* this loop must work, since GetCTEForRTE did */
for (levelsup = 0;
levelsup < rte->ctelevelsup + netlevelsup;
* Recurse into the sub-select to see what its Var
* refers to. We have to build an additional level of
* namespace to keep in step with varlevelsup in the
- * subselect.
+ * subselect; furthermore, the subquery RTE might be
+ * from an outer query level, in which case the
+ * namespace for the subselect must have that outer
+ * level as parent namespace.
*/
+ List *save_nslist = context->namespaces;
+ List *parent_namespaces;
deparse_namespace mydpns;
const char *result;
+ parent_namespaces = list_copy_tail(context->namespaces,
+ netlevelsup);
+
set_deparse_for_query(&mydpns, rte->subquery,
- context->namespaces);
+ parent_namespaces);
- context->namespaces = lcons(&mydpns,
- context->namespaces);
+ context->namespaces = lcons(&mydpns, parent_namespaces);
result = get_name_for_var_field((Var *) expr, fieldno,
0, context);
- context->namespaces =
- list_delete_first(context->namespaces);
+ context->namespaces = save_nslist;
return result;
}
attnum);
if (ste == NULL || ste->resjunk)
- elog(ERROR, "subquery %s does not have attribute %d",
+ elog(ERROR, "CTE %s does not have attribute %d",
rte->eref->aliasname, attnum);
expr = (Node *) ste->expr;
if (IsA(expr, Var))
/*
* Recurse into the CTE to see what its Var refers to.
* We have to build an additional level of namespace
- * to keep in step with varlevelsup in the CTE.
- * Furthermore it could be an outer CTE, so we may
- * have to delete some levels of namespace.
+ * to keep in step with varlevelsup in the CTE;
+ * furthermore it could be an outer CTE (compare
+ * SUBQUERY case above).
*/
List *save_nslist = context->namespaces;
- List *new_nslist;
+ List *parent_namespaces;
deparse_namespace mydpns;
const char *result;
+ parent_namespaces = list_copy_tail(context->namespaces,
+ ctelevelsup);
+
set_deparse_for_query(&mydpns, ctequery,
- context->namespaces);
+ parent_namespaces);
- new_nslist = list_copy_tail(context->namespaces,
- ctelevelsup);
- context->namespaces = lcons(&mydpns, new_nslist);
+ context->namespaces = lcons(&mydpns, parent_namespaces);
result = get_name_for_var_field((Var *) expr, fieldno,
0, context);
(,) | t | f
(6 rows)
+--
+-- Check parsing of indirect references to composite values (bug #18077)
+--
+explain (verbose, costs off)
+with cte(c) as materialized (select row(1, 2)),
+ cte2(c) as (select * from cte)
+select * from cte2 as t
+where (select * from (select c as c1) s
+ where (select (c1).f1 > 0)) is not null;
+ QUERY PLAN
+--------------------------------------------
+ CTE Scan on cte
+ Output: cte.c
+ Filter: ((SubPlan 3) IS NOT NULL)
+ CTE cte
+ -> Result
+ Output: '(1,2)'::record
+ SubPlan 3
+ -> Result
+ Output: cte.c
+ One-Time Filter: $2
+ InitPlan 2 (returns $2)
+ -> Result
+ Output: ((cte.c).f1 > 0)
+(13 rows)
+
+with cte(c) as materialized (select row(1, 2)),
+ cte2(c) as (select * from cte)
+select * from cte2 as t
+where (select * from (select c as c1) s
+ where (select (c1).f1 > 0)) is not null;
+ c
+-------
+ (1,2)
+(1 row)
+
+-- Also check deparsing of such cases
+create view composite_v as
+with cte(c) as materialized (select row(1, 2)),
+ cte2(c) as (select * from cte)
+select 1 as one from cte2 as t
+where (select * from (select c as c1) s
+ where (select (c1).f1 > 0)) is not null;
+select pg_get_viewdef('composite_v', true);
+ pg_get_viewdef
+--------------------------------------------------------
+ WITH cte(c) AS MATERIALIZED ( +
+ SELECT ROW(1, 2) AS "row" +
+ ), cte2(c) AS ( +
+ SELECT cte.c +
+ FROM cte +
+ ) +
+ SELECT 1 AS one +
+ FROM cte2 t +
+ WHERE (( SELECT s.c1 +
+ FROM ( SELECT t.c AS c1) s +
+ WHERE ( SELECT (s.c1).f1 > 0))) IS NOT NULL;
+(1 row)
+
+drop view composite_v;
--
-- Tests for component access / FieldSelect
--
(null,row(1,2)), (null,row(null,null)), (null,null) )
select r, r is null as isnull, r is not null as isnotnull from r;
+--
+-- Check parsing of indirect references to composite values (bug #18077)
+--
+explain (verbose, costs off)
+with cte(c) as materialized (select row(1, 2)),
+ cte2(c) as (select * from cte)
+select * from cte2 as t
+where (select * from (select c as c1) s
+ where (select (c1).f1 > 0)) is not null;
+
+with cte(c) as materialized (select row(1, 2)),
+ cte2(c) as (select * from cte)
+select * from cte2 as t
+where (select * from (select c as c1) s
+ where (select (c1).f1 > 0)) is not null;
+
+-- Also check deparsing of such cases
+create view composite_v as
+with cte(c) as materialized (select row(1, 2)),
+ cte2(c) as (select * from cte)
+select 1 as one from cte2 as t
+where (select * from (select c as c1) s
+ where (select (c1).f1 > 0)) is not null;
+select pg_get_viewdef('composite_v', true);
+drop view composite_v;
--
-- Tests for component access / FieldSelect