/*------------------------------------------------------------------------- * * 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, ...) * * We also support "WHERE CURRENT OF cursor" conditions (CurrentOfExpr), * which amount to "CTID = run-time-determined-TID". These could in * theory be translated to a simple comparison of CTID to the result of * a function, but in practice it works better to keep the special node * representation all the way through to execution. * * 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) 1996-2011, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/optimizer/path/tidpath.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/sysattr.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/pathnode.h" #include "optimizer/paths.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 */ if (((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). */ void create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel) { List *tidquals; tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid); if (tidquals) add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals)); }