/*------------------------------------------------------------------------- * * tidpath.c * Routines to determine which tids are usable for scanning a * given relation, and create TidPaths accordingly. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.1 1999/11/23 20:06:55 momjian Exp $ * *------------------------------------------------------------------------- */ #include #include "postgres.h" #include "access/heapam.h" #include "catalog/catname.h" #include "catalog/pg_amop.h" #include "catalog/pg_operator.h" #include "executor/executor.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_oper.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" static List *create_tidscan_joinpaths(RelOptInfo *); static List *TidqualFromRestrictinfo(List *relids, List * restrictinfo); static bool isEvaluable(int varno, Node *node); static Node *TidequalClause(int varno, Expr *node); static List *TidqualFromExpr(int varno, Expr *expr); static bool isEvaluable(int varno, Node *node) { List *lst; Expr *expr; if (IsA(node, Const)) return true; if (IsA(node, Param)) return true; if (IsA(node, Var)) { Var *var = (Var *)node; if (var->varno == varno) return false; return true; } if (!is_funcclause(node)) return false; expr = (Expr *)node; foreach (lst, expr->args) { if (!isEvaluable(varno, lfirst(lst))) return false; } return true; } /* * The 2nd parameter should be an opclause * Extract the right node if the opclause is CTID= .... * or the left node if the opclause is ....=CTID */ static Node *TidequalClause(int varno, Expr *node) { Node *rnode = 0, *arg1, *arg2, *arg; Oper *oper; Var *var; Const *aconst; Param *param; Expr *expr; if (!node->oper) return rnode; if (!node->args) return rnode; if (length(node->args) != 2) return rnode; oper = (Oper *) node->oper; if (oper->opno != TIDEqualOperator) return rnode; arg1 = lfirst(node->args); arg2 = lsecond(node->args); arg = (Node *)0; if (IsA(arg1, Var)) { var = (Var *) arg1; if (var->varno == varno && var->varattno == SelfItemPointerAttributeNumber && var->vartype == TIDOID) arg = arg2; else if (var->varnoold == varno && var->varoattno == SelfItemPointerAttributeNumber && var->vartype == TIDOID) arg = arg2; } if ((!arg) && IsA(arg2, Var)) { var = (Var *) arg2; if (var->varno == varno && var->varattno == SelfItemPointerAttributeNumber && var->vartype == TIDOID) arg = arg1; } if (!arg) return rnode; switch (nodeTag(arg)) { case T_Const: aconst = (Const *) arg; if (aconst->consttype != TIDOID) return rnode; if (aconst->constbyval) return rnode; rnode = arg; break; case T_Param: param = (Param *) arg; if (param->paramtype != TIDOID) return rnode; rnode = arg; break; case T_Var: var = (Var *) arg; if (var->varno == varno || var->vartype != TIDOID) return rnode; rnode = arg; break; case T_Expr: expr = (Expr *) arg; if (expr->typeOid != TIDOID) return rnode; if (expr->opType != FUNC_EXPR) return rnode; if (isEvaluable(varno, (Node *)expr)) rnode = arg; break; default: break; } return rnode; } /* * Extract the list of CTID values from a specified expr node. * When the expr node is an or_clause,we try to extract CTID * values from all member nodes. However we would discard them * all if we couldn't extract CTID values from a member node. * When the expr node is an and_clause,we return the list of * CTID values if we could extract the CTID values from a member * node. */ static List *TidqualFromExpr(int varno, Expr *expr) { List *rlst = NIL, *lst, *frtn; Node *node = (Node *) expr, *rnode; if (is_opclause(node)) { rnode = TidequalClause(varno, expr); if (rnode) { rlst = lcons(rnode, rlst); } } else if (and_clause(node)) { foreach (lst, expr->args) { node = lfirst(lst); if (!IsA(node, Expr)) continue; rlst = TidqualFromExpr(varno, (Expr *)node); if (rlst) break; } } else if (or_clause(node)) { foreach (lst, expr->args) { node = lfirst(lst); if (IsA(node, Expr) && (frtn = TidqualFromExpr(varno, (Expr *)node)) ) { rlst = nconc(rlst, frtn); } else { if (rlst) freeList(rlst); rlst = NIL; break; } } } return rlst; } static List *TidqualFromRestrictinfo(List *relids, List * restrictinfo) { List *lst, *rlst = NIL; int varno; Node *node; Expr *expr; if (length(relids)>1) return NIL; varno = (int)lfirst(relids); foreach (lst, restrictinfo) { node = lfirst(lst); if (!IsA(node, RestrictInfo)) continue; expr = ((RestrictInfo *)node)->clause; rlst = TidqualFromExpr(varno, expr); if (rlst) { break; } } return rlst; } /* * create_tidscan_joinpaths * Creates a path corresponding to a tid_direct scan, returning the * pathnode. * */ List * create_tidscan_joinpaths(RelOptInfo *rel) { List *rlst = NIL, *lst; TidPath *pathnode = (TidPath *)0; List *restinfo, *tideval; foreach (lst, rel->joininfo) { JoinInfo *joininfo = (JoinInfo *)lfirst(lst); restinfo = joininfo->jinfo_restrictinfo; tideval = TidqualFromRestrictinfo(rel->relids, restinfo); if (tideval && length(tideval) == 1) { pathnode = makeNode(TidPath); pathnode->path.pathtype = T_TidScan; pathnode->path.parent = rel; pathnode->path.path_cost = 0.0; pathnode->path.pathkeys = NIL; pathnode->path.path_cost = cost_tidscan(tideval); pathnode->tideval = tideval; /* pathnode->tideval = copyObject(tideval); freeList(tideval); */ pathnode->unjoined_relids = joininfo->unjoined_relids; rlst = lappend(rlst, pathnode); } } rel->innerjoin = nconc(rel->innerjoin, rlst); return rlst; } /* * create_tidscan_paths * Creates a path corresponding to a tid direct scan, returning the * pathnode List. * */ List * create_tidscan_paths(Query *root, RelOptInfo *rel) { List *rlst = NIL; TidPath *pathnode = (TidPath *)0; List *tideval = TidqualFromRestrictinfo(rel->relids, rel->restrictinfo); if (tideval) pathnode = create_tidscan_path(rel, tideval); if (pathnode) rlst = lcons(pathnode, rlst); create_tidscan_joinpaths(rel); return rlst; }