mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Fix index-only scan plans when not all index columns can be returned.
If an index has both returnable and non-returnable columns, and one of the non-returnable columns is an expression using a Var that is in a returnable column, then a query returning that expression could result in an index-only scan plan that attempts to read the non-returnable column, instead of recomputing the expression from the returnable column as intended. To fix, redefine the "indextlist" list of an IndexOnlyScan plan node as containing null Consts in place of any non-returnable columns. This solves the problem by preventing setrefs.c from falsely matching to such entries. The executor is happy since it only cares about the exposed types of the entries, and ruleutils.c doesn't care because a correct plan won't reference those entries. I considered some other ways to prevent setrefs.c from doing the wrong thing, but this way seems good since (a) it allows a very localized fix, (b) it makes the indextlist structure more compact in many cases, and (c) the indextlist is now a more faithful representation of what the index AM will actually produce, viz. nulls for any non-returnable columns. This is easier to hit since we introduced included columns, but it's possible to construct failing examples without that, as per the added regression test. Hence, back-patch to all supported branches. Per bug #17350 from Louis Jachiet. Discussion: https://postgr.es/m/17350-b5bdcf476e5badbb@postgresql.org
This commit is contained in:
@ -20,6 +20,7 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "access/sysattr.h"
|
||||
#include "catalog/pg_am.h"
|
||||
#include "catalog/pg_class.h"
|
||||
#include "foreign/fdwapi.h"
|
||||
#include "miscadmin.h"
|
||||
@ -191,6 +192,7 @@ static IndexOnlyScan *make_indexonlyscan(List *qptlist, List *qpqual,
|
||||
List *indexqual, List *indexorderby,
|
||||
List *indextlist,
|
||||
ScanDirection indexscandir);
|
||||
static List *make_indexonly_tlist(IndexOptInfo *indexinfo);
|
||||
static BitmapIndexScan *make_bitmap_indexscan(Index scanrelid, Oid indexid,
|
||||
List *indexqual,
|
||||
List *indexqualorig);
|
||||
@ -621,7 +623,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
|
||||
if (best_path->pathtype == T_IndexOnlyScan)
|
||||
{
|
||||
/* For index-only scan, the preferred tlist is the index's */
|
||||
tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);
|
||||
tlist = copyObject(make_indexonly_tlist(((IndexPath *) best_path)->indexinfo));
|
||||
|
||||
/*
|
||||
* Transfer sortgroupref data to the replacement tlist, if
|
||||
@ -3070,7 +3072,7 @@ create_indexscan_plan(PlannerInfo *root,
|
||||
indexoid,
|
||||
fixed_indexquals,
|
||||
fixed_indexorderbys,
|
||||
best_path->indexinfo->indextlist,
|
||||
make_indexonly_tlist(best_path->indexinfo),
|
||||
best_path->indexscandir);
|
||||
else
|
||||
scan_plan = (Scan *) make_indexscan(tlist,
|
||||
@ -5462,6 +5464,53 @@ make_indexonlyscan(List *qptlist,
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
* make_indexonly_tlist
|
||||
*
|
||||
* Construct the indextlist for an IndexOnlyScan plan node.
|
||||
* We must replace any column that can't be returned by the index AM
|
||||
* with a null Const of the appropriate datatype. This is necessary
|
||||
* to prevent setrefs.c from trying to use the value of such a column,
|
||||
* and anyway it makes the indextlist a better representative of what
|
||||
* the indexscan will really return. (We do this here, not where the
|
||||
* IndexOptInfo is originally constructed, because earlier planner
|
||||
* steps need to know what is in such columns.)
|
||||
*/
|
||||
static List *
|
||||
make_indexonly_tlist(IndexOptInfo *indexinfo)
|
||||
{
|
||||
List *result;
|
||||
int i;
|
||||
ListCell *lc;
|
||||
|
||||
/* We needn't work hard for the common case of btrees. */
|
||||
if (indexinfo->relam == BTREE_AM_OID)
|
||||
return indexinfo->indextlist;
|
||||
|
||||
result = NIL;
|
||||
i = 0;
|
||||
foreach(lc, indexinfo->indextlist)
|
||||
{
|
||||
TargetEntry *indextle = (TargetEntry *) lfirst(lc);
|
||||
|
||||
if (indexinfo->canreturn[i])
|
||||
result = lappend(result, indextle);
|
||||
else
|
||||
{
|
||||
TargetEntry *newtle = makeNode(TargetEntry);
|
||||
Node *texpr = (Node *) indextle->expr;
|
||||
|
||||
memcpy(newtle, indextle, sizeof(TargetEntry));
|
||||
newtle->expr = (Expr *) makeNullConst(exprType(texpr),
|
||||
exprTypmod(texpr),
|
||||
exprCollation(texpr));
|
||||
result = lappend(result, newtle);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static BitmapIndexScan *
|
||||
make_bitmap_indexscan(Index scanrelid,
|
||||
Oid indexid,
|
||||
|
Reference in New Issue
Block a user