Re: varattno remapping

From: Craig Ringer <craig(at)2ndquadrant(dot)com>
To: Abbas Butt <abbas(dot)butt(at)enterprisedb(dot)com>
Cc: PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: varattno remapping
Date: 2013-12-24 10:46:22
Message-ID: 52B965FE.5020703@2ndquadrant.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 12/24/2013 03:21 PM, Abbas Butt wrote:

> Could you please explain a little bit more how would you solve the posed
> problem using map_variable_attnos?
>
> I was recently working on a similar problem and used the following algo
> to solve it.
>
> I had to find to which column of the base table does a column in
> the select statement of the view query belong.
> To relate a target list entry in the select query of the view to
> an actual column in base table.

Sounds similar. My problem is simplified by the constraint that the view
must be a simple view (only one base relation) and must only contain
simple column-references to single the base relation. No expressions,
no joins. (These are the rules for simply updatable views).

I'm new to the planner and rewriter, so don't take what I do as any kind
of example beyond "this seems to work".

I generate a varattno mapping as follows:

/*
* Scan the passed view target list, whose members must consist solely
* of Var nodes with a varno equal to the passed targetvarno.
*
* A mapping is built from the resno (i.e. tlist index) of the view
* tlist to the corresponding attribute number of the base relation
* the varattno points to.
*
* Must not be called with a targetlist containing non-Var entries.
*/
static void
gen_view_base_attr_map(List *viewtlist, AttrNumber * attnomap, int
targetvarno)
{
ListCell *lc;
TargetEntry *te;
Var *tev;
int l_viewtlist = list_length(viewtlist);

foreach(lc, viewtlist)
{
te = (TargetEntry*) lfirst(lc);
/* Could relax this in future and map only the var entries,
* ignoring everything else, but currently pointless since we
* are only interested in simple views. */
Assert(IsA(te->expr, Var));
tev = (Var*) te->expr;
Assert(tev->varno == targetvarno);
Assert(te->resno - 1 < l_viewtlist);
attnomap[te->resno - 1] = tev->varattno;
}
}

producing a forward mapping of view attno to base relation attno.

I then apply the varattno remapping, in this case to the returning list
of a DML query acting on a view, with something like:

varattno_map = palloc( list_length(viewquery->targetList) *
sizeof(AttrNumber) );

gen_view_base_attr_map(viewquery->targetList,
varattno_map, rtr->rtindex);

parsetree->returningList = map_variable_attnos(
(Node*) parsetree->returningList,
old_result_rt_index, 0,
varattno_map, list_length(viewquery->targetList),
&found_whole_row_var
);

if (found_whole_row_var)
{
/* TODO: Extend map_variable_attnos API to
pass a mutator to handle whole-row vars. */
elog(ERROR, "RETURNING list contains a whole-row variable, "
"which is not currently supported for updatable "
"views");
}

ChangeVarNodes((Node*) parsetree->returningList,
old_result_rt_index,
new_result_rt_index,
0);

I'd prefer to be doing the map_variable_attnos and ChangeVarNodes work
in a single pass, but it looks like a clumsy and verbose process to
write a new walker. So I'm going to leave it as an "opportunity for
future optimisation" for now ;-)

(As it happens that "found_whole_row_var" is a real pain; I'm going to
have to deal with a TODO item in map_variable_attnos to provide a
callback that replaces a whole-row Var with an expansion of it into a
row-expression).

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Atri Sharma 2013-12-24 11:07:21 Re: WITHIN GROUP patch
Previous Message Fabrízio de Royes Mello 2013-12-24 10:40:35 Re: trailing comment ghost-timing