1999-11-23 21:07:06 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* tidpath.c
|
2005-08-23 22:49:47 +02:00
|
|
|
* Routines to determine which TID conditions are usable for scanning
|
|
|
|
* a given relation, and create TidPaths accordingly.
|
|
|
|
*
|
|
|
|
* What we are looking for here is WHERE conditions of the form
|
|
|
|
* "CTID = pseudoconstant", which can be implemented by just fetching
|
2005-11-26 23:14:57 +01:00
|
|
|
* the tuple directly via heap_fetch(). We can also handle OR'd conditions
|
|
|
|
* such as (CTID = const1) OR (CTID = const2), as well as ScalarArrayOpExpr
|
|
|
|
* conditions of the form CTID = ANY(pseudoconstant_array). In particular
|
|
|
|
* this allows
|
2005-08-23 22:49:47 +02:00
|
|
|
* WHERE ctid IN (tid1, tid2, ...)
|
|
|
|
*
|
|
|
|
* There is currently no special support for joins involving CTID; in
|
2005-10-15 04:49:52 +02:00
|
|
|
* particular nothing corresponding to best_inner_indexscan(). Since it's
|
2005-08-23 22:49:47 +02:00
|
|
|
* not very useful to store TIDs of one table in another table, there
|
|
|
|
* doesn't seem to be enough use-case to justify adding a lot of code
|
|
|
|
* for that.
|
|
|
|
*
|
1999-11-23 21:07:06 +01:00
|
|
|
*
|
2006-03-05 16:59:11 +01:00
|
|
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1999-11-23 21:07:06 +01:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2006-03-05 16:59:11 +01:00
|
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/tidpath.c,v 1.27 2006/03/05 15:58:28 momjian Exp $
|
1999-11-23 21:07:06 +01:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2005-08-23 22:49:47 +02:00
|
|
|
#include "access/htup.h"
|
1999-11-23 21:07:06 +01:00
|
|
|
#include "catalog/pg_operator.h"
|
2005-08-23 22:49:47 +02:00
|
|
|
#include "catalog/pg_type.h"
|
1999-11-23 21:07:06 +01:00
|
|
|
#include "optimizer/clauses.h"
|
|
|
|
#include "optimizer/pathnode.h"
|
|
|
|
#include "optimizer/paths.h"
|
2005-08-23 22:49:47 +02:00
|
|
|
#include "parser/parse_expr.h"
|
2005-06-06 00:32:58 +02:00
|
|
|
|
1999-11-23 21:07:06 +01:00
|
|
|
|
2005-11-26 23:14:57 +01:00
|
|
|
static bool IsTidEqualClause(OpExpr *node, int varno);
|
|
|
|
static bool IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno);
|
|
|
|
static List *TidQualFromExpr(Node *expr, int varno);
|
|
|
|
static List *TidQualFromRestrictinfo(List *restrictinfo, int varno);
|
2005-06-06 00:32:58 +02:00
|
|
|
|
1999-11-23 21:07:06 +01:00
|
|
|
|
|
|
|
/*
|
2005-08-23 22:49:47 +02:00
|
|
|
* Check to see if an opclause is of the form
|
|
|
|
* CTID = pseudoconstant
|
|
|
|
* or
|
|
|
|
* pseudoconstant = CTID
|
|
|
|
*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We check that the CTID Var belongs to relation "varno". That is probably
|
2005-08-23 22:49:47 +02:00
|
|
|
* redundant considering this is only applied to restriction clauses, but
|
|
|
|
* let's be safe.
|
1999-11-23 21:07:06 +01:00
|
|
|
*/
|
2005-11-26 23:14:57 +01:00
|
|
|
static bool
|
|
|
|
IsTidEqualClause(OpExpr *node, int varno)
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
2005-08-23 22:49:47 +02:00
|
|
|
Node *arg1,
|
2000-04-12 19:17:23 +02:00
|
|
|
*arg2,
|
2005-08-23 22:49:47 +02:00
|
|
|
*other;
|
2000-04-12 19:17:23 +02:00
|
|
|
Var *var;
|
1999-11-23 21:07:06 +01:00
|
|
|
|
2005-08-23 22:49:47 +02:00
|
|
|
/* Operator must be tideq */
|
2002-12-12 16:49:42 +01:00
|
|
|
if (node->opno != TIDEqualOperator)
|
2005-11-26 23:14:57 +01:00
|
|
|
return false;
|
2004-05-31 01:40:41 +02:00
|
|
|
if (list_length(node->args) != 2)
|
2005-11-26 23:14:57 +01:00
|
|
|
return false;
|
2004-05-26 06:41:50 +02:00
|
|
|
arg1 = linitial(node->args);
|
1999-11-23 21:07:06 +01:00
|
|
|
arg2 = lsecond(node->args);
|
|
|
|
|
2005-08-23 22:49:47 +02:00
|
|
|
/* Look for CTID as either argument */
|
|
|
|
other = NULL;
|
|
|
|
if (arg1 && IsA(arg1, Var))
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
|
|
|
var = (Var *) arg1;
|
2005-08-23 22:49:47 +02:00
|
|
|
if (var->varattno == SelfItemPointerAttributeNumber &&
|
|
|
|
var->vartype == TIDOID &&
|
|
|
|
var->varno == varno &&
|
|
|
|
var->varlevelsup == 0)
|
|
|
|
other = arg2;
|
1999-11-23 21:07:06 +01:00
|
|
|
}
|
2005-08-23 22:49:47 +02:00
|
|
|
if (!other && arg2 && IsA(arg2, Var))
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
|
|
|
var = (Var *) arg2;
|
2005-08-23 22:49:47 +02:00
|
|
|
if (var->varattno == SelfItemPointerAttributeNumber &&
|
|
|
|
var->vartype == TIDOID &&
|
|
|
|
var->varno == varno &&
|
|
|
|
var->varlevelsup == 0)
|
|
|
|
other = arg1;
|
1999-11-23 21:07:06 +01:00
|
|
|
}
|
2005-08-23 22:49:47 +02:00
|
|
|
if (!other)
|
2005-11-26 23:14:57 +01:00
|
|
|
return false;
|
2005-08-23 22:49:47 +02:00
|
|
|
if (exprType(other) != TIDOID)
|
2005-11-26 23:14:57 +01:00
|
|
|
return false; /* probably can't happen */
|
2005-08-23 22:49:47 +02:00
|
|
|
|
|
|
|
/* The other argument must be a pseudoconstant */
|
|
|
|
if (!is_pseudo_constant_clause(other))
|
2005-11-26 23:14:57 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true; /* success */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to see if a clause is of the form
|
|
|
|
* CTID = ANY (pseudoconstant_array)
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno)
|
|
|
|
{
|
|
|
|
Node *arg1,
|
|
|
|
*arg2;
|
|
|
|
|
|
|
|
/* Operator must be tideq */
|
|
|
|
if (node->opno != TIDEqualOperator)
|
|
|
|
return false;
|
|
|
|
if (!node->useOr)
|
|
|
|
return false;
|
|
|
|
Assert(list_length(node->args) == 2);
|
|
|
|
arg1 = linitial(node->args);
|
|
|
|
arg2 = lsecond(node->args);
|
|
|
|
|
|
|
|
/* CTID must be first argument */
|
|
|
|
if (arg1 && IsA(arg1, Var))
|
|
|
|
{
|
|
|
|
Var *var = (Var *) arg1;
|
2005-08-23 22:49:47 +02:00
|
|
|
|
2005-11-26 23:14:57 +01:00
|
|
|
if (var->varattno == SelfItemPointerAttributeNumber &&
|
|
|
|
var->vartype == TIDOID &&
|
|
|
|
var->varno == varno &&
|
|
|
|
var->varlevelsup == 0)
|
|
|
|
{
|
|
|
|
/* The other argument must be a pseudoconstant */
|
|
|
|
if (is_pseudo_constant_clause(arg2))
|
|
|
|
return true; /* success */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
1999-11-23 21:07:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-08-23 22:49:47 +02:00
|
|
|
* Extract a set of CTID conditions from the given qual expression
|
|
|
|
*
|
2005-11-26 23:14:57 +01:00
|
|
|
* Returns a List of CTID qual expressions (with implicit OR semantics
|
|
|
|
* across the list), or NIL if there are no usable conditions.
|
|
|
|
*
|
2005-08-23 22:49:47 +02:00
|
|
|
* If the expression is an AND clause, we can use a CTID condition
|
|
|
|
* from any sub-clause. If it is an OR clause, we must be able to
|
|
|
|
* extract a CTID condition from every sub-clause, or we can't use it.
|
|
|
|
*
|
|
|
|
* In theory, in the AND case we could get CTID conditions from different
|
|
|
|
* sub-clauses, in which case we could try to pick the most efficient one.
|
|
|
|
* In practice, such usage seems very unlikely, so we don't bother; we
|
|
|
|
* just exit as soon as we find the first candidate.
|
2000-04-12 19:17:23 +02:00
|
|
|
*/
|
2002-12-12 16:49:42 +01:00
|
|
|
static List *
|
2005-11-26 23:14:57 +01:00
|
|
|
TidQualFromExpr(Node *expr, int varno)
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
2005-11-26 23:14:57 +01:00
|
|
|
List *rlst = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
1999-11-23 21:07:06 +01:00
|
|
|
|
2005-08-23 22:49:47 +02:00
|
|
|
if (is_opclause(expr))
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
2005-08-23 22:49:47 +02:00
|
|
|
/* base case: check for tideq opclause */
|
2005-11-26 23:14:57 +01:00
|
|
|
if (IsTidEqualClause((OpExpr *) expr, varno))
|
|
|
|
rlst = list_make1(expr);
|
|
|
|
}
|
|
|
|
else if (expr && IsA(expr, ScalarArrayOpExpr))
|
|
|
|
{
|
|
|
|
/* another base case: check for tid = ANY clause */
|
|
|
|
if (IsTidEqualAnyClause((ScalarArrayOpExpr *) expr, varno))
|
|
|
|
rlst = list_make1(expr);
|
1999-11-23 21:07:06 +01:00
|
|
|
}
|
2005-08-23 22:49:47 +02:00
|
|
|
else if (and_clause(expr))
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(l, ((BoolExpr *) expr)->args)
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
2005-11-26 23:14:57 +01:00
|
|
|
rlst = TidQualFromExpr((Node *) lfirst(l), varno);
|
1999-11-23 21:07:06 +01:00
|
|
|
if (rlst)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-08-23 22:49:47 +02:00
|
|
|
else if (or_clause(expr))
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(l, ((BoolExpr *) expr)->args)
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
2005-11-26 23:14:57 +01:00
|
|
|
List *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
|
|
|
|
|
2002-12-12 16:49:42 +01:00
|
|
|
if (frtn)
|
2004-05-31 01:40:41 +02:00
|
|
|
rlst = list_concat(rlst, frtn);
|
1999-11-23 21:07:06 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (rlst)
|
2004-05-31 01:40:41 +02:00
|
|
|
list_free(rlst);
|
1999-11-23 21:07:06 +01:00
|
|
|
rlst = NIL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rlst;
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-11-23 21:07:06 +01:00
|
|
|
|
2005-08-23 22:49:47 +02:00
|
|
|
/*
|
|
|
|
* Extract a set of CTID conditions from the given restrictinfo list
|
|
|
|
*
|
|
|
|
* This is essentially identical to the AND case of TidQualFromExpr,
|
|
|
|
* except for the format of the input.
|
|
|
|
*/
|
2000-02-07 05:41:04 +01:00
|
|
|
static List *
|
2005-11-26 23:14:57 +01:00
|
|
|
TidQualFromRestrictinfo(List *restrictinfo, int varno)
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
List *rlst = NIL;
|
2005-08-23 22:49:47 +02:00
|
|
|
ListCell *l;
|
1999-11-23 21:07:06 +01:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(l, restrictinfo)
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
2005-08-23 22:49:47 +02:00
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
|
|
|
|
|
|
|
if (!IsA(rinfo, RestrictInfo))
|
|
|
|
continue; /* probably should never happen */
|
2005-11-26 23:14:57 +01:00
|
|
|
rlst = TidQualFromExpr((Node *) rinfo->clause, varno);
|
1999-11-23 21:07:06 +01:00
|
|
|
if (rlst)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return rlst;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create_tidscan_paths
|
2005-08-23 22:49:47 +02:00
|
|
|
* Create paths corresponding to direct TID scans of the given rel.
|
|
|
|
*
|
2000-02-15 21:49:31 +01:00
|
|
|
* Candidate paths are added to the rel's pathlist (using add_path).
|
1999-11-23 21:07:06 +01:00
|
|
|
*/
|
2000-02-15 21:49:31 +01:00
|
|
|
void
|
2005-06-06 00:32:58 +02:00
|
|
|
create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
|
1999-11-23 21:07:06 +01:00
|
|
|
{
|
2005-11-26 23:14:57 +01:00
|
|
|
List *tidquals;
|
2005-08-23 22:49:47 +02:00
|
|
|
|
2005-11-26 23:14:57 +01:00
|
|
|
tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2005-11-26 23:14:57 +01:00
|
|
|
if (tidquals)
|
|
|
|
add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals));
|
1999-11-23 21:07:06 +01:00
|
|
|
}
|