blob: d3cad47a8367ad0cdf8f256eb24348d155f7dc39 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*-------------------------------------------------------------------------
*
* tidpath.c
* 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
* 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
* WHERE ctid IN (tid1, tid2, ...)
*
* There is currently no special support for joins involving CTID; in
* particular nothing corresponding to best_inner_indexscan(). Since it's
* 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.
*
*
* Portions Copyright (c) 2007-2008, Greenplum inc
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/tidpath.c,v 1.28 2006/10/04 00:29:54 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/htup.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "optimizer/clauses.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "parser/parse_expr.h"
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);
/*
* Check to see if an opclause is of the form
* CTID = pseudoconstant
* or
* pseudoconstant = CTID
*
* We check that the CTID Var belongs to relation "varno". That is probably
* redundant considering this is only applied to restriction clauses, but
* let's be safe.
*/
static bool
IsTidEqualClause(OpExpr *node, int varno)
{
Node *arg1,
*arg2,
*other;
Var *var;
/* Operator must be tideq */
if (node->opno != TIDEqualOperator)
return false;
if (list_length(node->args) != 2)
return false;
arg1 = linitial(node->args);
arg2 = lsecond(node->args);
/* Look for CTID as either argument */
other = NULL;
if (arg1 && IsA(arg1, Var))
{
var = (Var *) arg1;
if (var->varattno == SelfItemPointerAttributeNumber &&
var->vartype == TIDOID &&
var->varno == varno &&
var->varlevelsup == 0)
other = arg2;
}
if (!other && arg2 && IsA(arg2, Var))
{
var = (Var *) arg2;
if (var->varattno == SelfItemPointerAttributeNumber &&
var->vartype == TIDOID &&
var->varno == varno &&
var->varlevelsup == 0)
other = arg1;
}
if (!other)
return false;
if (exprType(other) != TIDOID)
return false; /* probably can't happen */
/* The other argument must be a pseudoconstant */
if (!is_pseudo_constant_clause(other))
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;
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;
}
/*
* Extract a set of CTID conditions from the given qual expression
*
* Returns a List of CTID qual expressions (with implicit OR semantics
* across the list), or NIL if there are no usable conditions.
*
* 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.
*/
static List *
TidQualFromExpr(Node *expr, int varno)
{
List *rlst = NIL;
ListCell *l;
if (is_opclause(expr))
{
/* base case: check for tideq opclause */
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);
}
else if (expr && IsA(expr, CurrentOfExpr))
{
/* another base case: check for CURRENT OF on this rel */
Insist(((CurrentOfExpr *) expr)->cvarno == varno);
rlst = list_make1(expr);
}
else if (and_clause(expr))
{
foreach(l, ((BoolExpr *) expr)->args)
{
rlst = TidQualFromExpr((Node *) lfirst(l), varno);
if (rlst)
break;
}
}
else if (or_clause(expr))
{
foreach(l, ((BoolExpr *) expr)->args)
{
List *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
if (frtn)
rlst = list_concat(rlst, frtn);
else
{
if (rlst)
list_free(rlst);
rlst = NIL;
break;
}
}
}
return rlst;
}
/*
* 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.
*/
static List *
TidQualFromRestrictinfo(List *restrictinfo, int varno)
{
List *rlst = NIL;
ListCell *l;
foreach(l, restrictinfo)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
if (!IsA(rinfo, RestrictInfo))
continue; /* probably should never happen */
rlst = TidQualFromExpr((Node *) rinfo->clause, varno);
if (rlst)
break;
}
return rlst;
}
/*
* create_tidscan_paths
* Create paths corresponding to direct TID scans of the given rel.
*
* Candidate paths are added to the rel's pathlist (using add_path).
*
* CDB: Instead of handing the paths to add_path(), we append them to a List
* (*ppathlist) belonging to the caller.
*
* CDB TODO: Set rel->onerow if at most one tid is to be fetched.
*/
void
create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel, List** ppathlist)
{
List *tidquals;
tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid);
if (tidquals)
*ppathlist = lappend(*ppathlist, create_tidscan_path(root, rel, tidquals));
}