diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ea7950ec502..626c11aa0c6 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * out of it's tuple * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.23 1999/08/25 23:21:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.24 1999/08/28 03:59:05 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -58,6 +58,11 @@ typedef struct QryHier Query *query; } QryHier; +typedef struct { + Index rt_index; + int levelsup; +} check_if_rte_used_context; + /* ---------- * Global data @@ -95,6 +100,7 @@ static char *get_select_query_def(Query *query, QryHier *qh); static char *get_insert_query_def(Query *query, QryHier *qh); static char *get_update_query_def(Query *query, QryHier *qh); static char *get_delete_query_def(Query *query, QryHier *qh); +static RangeTblEntry *get_rte_for_var(Var *var, QryHier *qh); static char *get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix); static char *get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix); static char *get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix); @@ -102,7 +108,9 @@ static char *get_const_expr(Const *constval); static char *get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix); static char *get_relation_name(Oid relid); static char *get_attribute_name(Oid relid, int2 attnum); -static bool check_if_rte_used(int rt_index, Node *node, int sup); +static bool check_if_rte_used(Node *node, Index rt_index, int levelsup); +static bool check_if_rte_used_walker(Node *node, + check_if_rte_used_context *context); /* ---------- @@ -853,7 +861,7 @@ get_select_query_def(Query *query, QryHier *qh) List *l; /* ---------- - * First we need need to know which and how many of the + * First we need to know which and how many of the * range table entries in the query are used in the target list * or queries qualification * ---------- @@ -862,21 +870,15 @@ get_select_query_def(Query *query, QryHier *qh) rt_used = palloc(sizeof(bool) * rt_length); for (i = 0; i < rt_length; i++) { - if (check_if_rte_used(i + 1, (Node *) (query->targetList), 0)) + if (check_if_rte_used((Node *) (query->targetList), i + 1, 0) || + check_if_rte_used(query->qual, i + 1, 0) || + check_if_rte_used(query->havingQual, i + 1, 0)) { rt_used[i] = TRUE; rt_numused++; } else - { - if (check_if_rte_used(i + 1, (Node *) (query->qual), 0)) - { - rt_used[i] = TRUE; - rt_numused++; - } - else - rt_used[i] = FALSE; - } + rt_used[i] = FALSE; } /* ---------- @@ -920,14 +922,14 @@ get_select_query_def(Query *query, QryHier *qh) strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1))); /* Check if we must say AS ... */ - if (nodeTag(tle->expr) != T_Var) + if (! IsA(tle->expr, Var)) tell_as = strcmp(tle->resdom->resname, "?column?"); else { Var *var = (Var *) (tle->expr); char *attname; - rte = (RangeTblEntry *) nth(var->varno - 1, query->rtable); + rte = get_rte_for_var(var, qh); attname = get_attribute_name(rte->relid, var->varattno); if (strcmp(attname, tle->resdom->resname)) tell_as = TRUE; @@ -990,9 +992,14 @@ get_select_query_def(Query *query, QryHier *qh) sep = ""; foreach(l, query->groupClause) { + GroupClause *grp = (GroupClause *) lfirst(l); + Node *groupexpr; + + groupexpr = get_sortgroupclause_expr(grp, + query->targetList); strcat(buf, sep); + strcat(buf, get_rule_expr(qh, 0, groupexpr, (rt_numused > 1))); sep = ", "; - strcat(buf, get_rule_expr(qh, 0, lfirst(l), (rt_numused > 1))); } } @@ -1032,21 +1039,15 @@ get_insert_query_def(Query *query, QryHier *qh) rt_used = palloc(sizeof(bool) * rt_length); for (i = 0; i < rt_length; i++) { - if (check_if_rte_used(i + 1, (Node *) (query->targetList), 0)) + if (check_if_rte_used((Node *) (query->targetList), i + 1, 0) || + check_if_rte_used(query->qual, i + 1, 0) || + check_if_rte_used(query->havingQual, i + 1, 0)) { rt_used[i] = TRUE; rt_numused++; } else - { - if (check_if_rte_used(i + 1, (Node *) (query->qual), 0)) - { - rt_used[i] = TRUE; - rt_numused++; - } - else - rt_used[i] = FALSE; - } + rt_used[i] = FALSE; } i = 0; @@ -1200,6 +1201,20 @@ get_delete_query_def(Query *query, QryHier *qh) return pstrdup(buf); } +/* + * Find the RTE referenced by a (possibly nonlocal) Var. + */ +static RangeTblEntry * +get_rte_for_var(Var *var, QryHier *qh) +{ + int sup = var->varlevelsup; + + while (sup-- > 0) + qh = qh->parent; + + return (RangeTblEntry *) nth(var->varno - 1, qh->query->rtable); +} + /* ---------- * get_rule_expr - Parse back an expression @@ -1216,19 +1231,112 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix) buf[0] = '\0'; /* ---------- - * Up to now I don't know if all the node types below - * can really occur in rules actions and qualifications. - * There might be some work left. + * Each level of get_rule_expr must return an indivisible term + * (parenthesized if necessary) to ensure result is reparsed into + * the same expression tree. + * + * There might be some work left here to support additional node types... * ---------- */ switch (nodeTag(node)) { - case T_TargetEntry: - { - TargetEntry *tle = (TargetEntry *) node; + case T_Const: + return get_const_expr((Const *) node); + break; - return get_rule_expr(qh, rt_index, - (Node *) (tle->expr), varprefix); + case T_Var: + { + Var *var = (Var *) node; + RangeTblEntry *rte = get_rte_for_var(var, qh); + + if (!strcmp(rte->refname, "*NEW*")) + strcat(buf, "new."); + else if (!strcmp(rte->refname, "*CURRENT*")) + strcat(buf, "old."); + else + { + strcat(buf, "\""); + strcat(buf, rte->refname); + strcat(buf, "\"."); + } + strcat(buf, "\""); + strcat(buf, get_attribute_name(rte->relid, var->varattno)); + strcat(buf, "\""); + + return pstrdup(buf); + } + break; + + case T_Expr: + { + Expr *expr = (Expr *) node; + + /* ---------- + * Expr nodes have to be handled a bit detailed + * ---------- + */ + switch (expr->opType) + { + case OP_EXPR: + strcat(buf, "("); + strcat(buf, get_rule_expr(qh, rt_index, + (Node *) get_leftop(expr), + varprefix)); + strcat(buf, " "); + strcat(buf, get_opname(((Oper *) expr->oper)->opno)); + strcat(buf, " "); + strcat(buf, get_rule_expr(qh, rt_index, + (Node *) get_rightop(expr), + varprefix)); + strcat(buf, ")"); + return pstrdup(buf); + break; + + case OR_EXPR: + strcat(buf, "("); + strcat(buf, get_rule_expr(qh, rt_index, + (Node *) get_leftop(expr), + varprefix)); + strcat(buf, " OR "); + strcat(buf, get_rule_expr(qh, rt_index, + (Node *) get_rightop(expr), + varprefix)); + strcat(buf, ")"); + return pstrdup(buf); + break; + + case AND_EXPR: + strcat(buf, "("); + strcat(buf, get_rule_expr(qh, rt_index, + (Node *) get_leftop(expr), + varprefix)); + strcat(buf, " AND "); + strcat(buf, get_rule_expr(qh, rt_index, + (Node *) get_rightop(expr), + varprefix)); + strcat(buf, ")"); + return pstrdup(buf); + break; + + case NOT_EXPR: + strcat(buf, "(NOT "); + strcat(buf, get_rule_expr(qh, rt_index, + (Node *) get_leftop(expr), + varprefix)); + strcat(buf, ")"); + return pstrdup(buf); + break; + + case FUNC_EXPR: + return get_func_expr(qh, rt_index, + (Expr *) node, + varprefix); + break; + + default: + printf("\n%s\n", nodeToString(node)); + elog(ERROR, "Expr type not supported"); + } } break; @@ -1246,127 +1354,58 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix) } break; - case T_GroupClause: + case T_ArrayRef: { - GroupClause *grp = (GroupClause *) node; - Node *groupexpr; + ArrayRef *aref = (ArrayRef *) node; + List *lowlist; + List *uplist; - groupexpr = get_sortgroupclause_expr(grp, - qh->query->targetList); - return get_rule_expr(qh, rt_index, groupexpr, varprefix); - } - break; - - case T_Expr: - { - Expr *expr = (Expr *) node; - - /* ---------- - * Expr nodes have to be handled a bit detailed - * ---------- - */ - switch (expr->opType) + strcat(buf, get_rule_expr(qh, rt_index, + aref->refexpr, varprefix)); + lowlist = aref->reflowerindexpr; + foreach(uplist, aref->refupperindexpr) { - case OP_EXPR: - strcat(buf, get_rule_expr(qh, rt_index, - (Node *) get_leftop(expr), - varprefix)); - strcat(buf, " "); - strcat(buf, get_opname(((Oper *) expr->oper)->opno)); - strcat(buf, " "); - strcat(buf, get_rule_expr(qh, rt_index, - (Node *) get_rightop(expr), - varprefix)); - return pstrdup(buf); - break; - - case OR_EXPR: - strcat(buf, "("); - strcat(buf, get_rule_expr(qh, rt_index, - (Node *) get_leftop(expr), - varprefix)); - strcat(buf, ") OR ("); - strcat(buf, get_rule_expr(qh, rt_index, - (Node *) get_rightop(expr), - varprefix)); - strcat(buf, ")"); - return pstrdup(buf); - break; - - case AND_EXPR: - strcat(buf, "("); - strcat(buf, get_rule_expr(qh, rt_index, - (Node *) get_leftop(expr), - varprefix)); - strcat(buf, ") AND ("); - strcat(buf, get_rule_expr(qh, rt_index, - (Node *) get_rightop(expr), - varprefix)); - strcat(buf, ")"); - return pstrdup(buf); - break; - - case NOT_EXPR: - strcat(buf, "NOT ("); - strcat(buf, get_rule_expr(qh, rt_index, - (Node *) get_leftop(expr), - varprefix)); - strcat(buf, ")"); - return pstrdup(buf); - break; - - case FUNC_EXPR: - return get_func_expr(qh, rt_index, - (Expr *) node, - varprefix); - break; - - default: - printf("\n%s\n", nodeToString(node)); - elog(ERROR, "Expr not yet supported"); - } - } - break; - - case T_Var: - { - Var *var = (Var *) node; - RangeTblEntry *rte; - int sup = var->varlevelsup; - - while (sup-- > 0) - qh = qh->parent; - - rte = (RangeTblEntry *) nth(var->varno - 1, qh->query->rtable); - - if (!strcmp(rte->refname, "*NEW*")) - strcat(buf, "new."); - else - { - if (!strcmp(rte->refname, "*CURRENT*")) - strcat(buf, "old."); - else + strcat(buf, "["); + if (lowlist) { - if (strcmp(rte->relname, rte->refname) != 0) - { - strcat(buf, "\""); - strcat(buf, rte->refname); - strcat(buf, "\"."); - } + strcat(buf, get_rule_expr(qh, rt_index, + (Node *) lfirst(lowlist), + varprefix)); + strcat(buf, ":"); + lowlist = lnext(lowlist); } + strcat(buf, get_rule_expr(qh, rt_index, + (Node *) lfirst(uplist), + varprefix)); + strcat(buf, "]"); } - strcat(buf, "\""); - strcat(buf, get_attribute_name(rte->relid, var->varattno)); - strcat(buf, "\""); - + /* XXX need to do anything with refassgnexpr? */ return pstrdup(buf); } break; - case T_List: + case T_CaseExpr: { - printf("\n%s\n", nodeToString(node)); - elog(ERROR, "List not yet supported"); + CaseExpr *caseexpr = (CaseExpr *) node; + List *temp; + + strcat(buf, "CASE"); + foreach(temp, caseexpr->args) + { + CaseWhen *when = (CaseWhen *) lfirst(temp); + + strcat(buf, " WHEN "); + strcat(buf, get_rule_expr(qh, rt_index, + when->expr, varprefix)); + strcat(buf, " THEN "); + strcat(buf, get_rule_expr(qh, rt_index, + when->result, varprefix)); + } + strcat(buf, " ELSE "); + strcat(buf, get_rule_expr(qh, rt_index, + caseexpr->defresult, varprefix)); + strcat(buf, " END"); + return pstrdup(buf); } break; @@ -1374,13 +1413,9 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix) return get_sublink_expr(qh, rt_index, node, varprefix); break; - case T_Const: - return get_const_expr((Const *) node); - break; - default: printf("\n%s\n", nodeToString(node)); - elog(ERROR, "get_ruledef of %s: unknown node type %d get_rule_expr()", + elog(ERROR, "get_ruledef of %s: unknown node type %d in get_rule_expr()", rulename, nodeTag(node)); break; } @@ -1423,7 +1458,7 @@ get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix) strcpy(buf, "("); strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args), varprefix)); - strcat(buf, ") ISNULL"); + strcat(buf, " ISNULL)"); return pstrdup(buf); } if (!strcmp(proname, "nonnullvalue")) @@ -1431,7 +1466,7 @@ get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix) strcpy(buf, "("); strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args), varprefix)); - strcat(buf, ") NOTNULL"); + strcat(buf, " NOTNULL)"); return pstrdup(buf); } } @@ -1475,10 +1510,11 @@ get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix) static char * get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix) { - HeapTuple proctup; - Form_pg_proc procStruct; - Expr *expr; + Expr *expr = (Expr *) (tle->expr); Func *func; + HeapTuple tup; + Form_pg_proc procStruct; + Form_pg_type typeStruct; Const *second_arg; /* ---------- @@ -1486,12 +1522,9 @@ get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix) * expression in the targetlist entry is a function call * ---------- */ - if (tle->resdom->restypmod < 0) - return get_rule_expr(qh, rt_index, tle->expr, varprefix); - if (nodeTag(tle->expr) != T_Expr) - return get_rule_expr(qh, rt_index, tle->expr, varprefix); - expr = (Expr *) (tle->expr); - if (expr->opType != FUNC_EXPR) + if (tle->resdom->restypmod < 0 || + ! IsA(expr, Expr) || + expr->opType != FUNC_EXPR) return get_rule_expr(qh, rt_index, tle->expr, varprefix); func = (Func *) (expr->oper); @@ -1500,12 +1533,11 @@ get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix) * Get the functions pg_proc tuple * ---------- */ - proctup = SearchSysCacheTuple(PROOID, - ObjectIdGetDatum(func->funcid), 0, 0, 0); - if (!HeapTupleIsValid(proctup)) + tup = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(func->funcid), 0, 0, 0); + if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup for proc %u failed", func->funcid); - - procStruct = (Form_pg_proc) GETSTRUCT(proctup); + procStruct = (Form_pg_proc) GETSTRUCT(tup); /* ---------- * It must be a function with two arguments where the first @@ -1513,22 +1545,34 @@ get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix) * an int4. * ---------- */ - if (procStruct->pronargs != 2) - return get_rule_expr(qh, rt_index, tle->expr, varprefix); - if (procStruct->prorettype != procStruct->proargtypes[0]) - return get_rule_expr(qh, rt_index, tle->expr, varprefix); - if (procStruct->proargtypes[1] != INT4OID) + if (procStruct->pronargs != 2 || + procStruct->prorettype != procStruct->proargtypes[0] || + procStruct->proargtypes[1] != INT4OID) return get_rule_expr(qh, rt_index, tle->expr, varprefix); + /* + * Furthermore, the name of the function must be the same + * as the argument/result type name. + */ + tup = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(procStruct->prorettype), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup for type %u failed", + procStruct->prorettype); + typeStruct = (Form_pg_type) GETSTRUCT(tup); + if (strncmp(procStruct->proname.data, typeStruct->typname.data, + NAMEDATALEN) != 0) + return get_rule_expr(qh, rt_index, tle->expr, varprefix); + /* ---------- * Finally (to be totally safe) the second argument must be a * const and match the value in the results atttypmod. * ---------- */ second_arg = (Const *) nth(1, expr->args); - if (nodeTag((Node *) second_arg) != T_Const) - return get_rule_expr(qh, rt_index, tle->expr, varprefix); - if ((int4) (second_arg->constvalue) != tle->resdom->restypmod) + if (! IsA(second_arg, Const) || + ((int4) second_arg->constvalue) != tle->resdom->restypmod) return get_rule_expr(qh, rt_index, tle->expr, varprefix); /* ---------- @@ -1556,12 +1600,12 @@ get_const_expr(Const *constval) char namebuf[64]; if (constval->constisnull) - return "NULL"; + return pstrdup("NULL"); typetup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(constval->consttype), 0, 0, 0); if (!HeapTupleIsValid(typetup)) - elog(ERROR, "cache lookup of type %d failed", constval->consttype); + elog(ERROR, "cache lookup of type %u failed", constval->consttype); typeStruct = (Form_pg_type) GETSTRUCT(typetup); @@ -1593,6 +1637,8 @@ get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix) buf[0] = '\0'; + strcat(buf, "("); + if (sublink->lefthand != NULL) { if (length(sublink->lefthand) > 1) @@ -1645,7 +1691,7 @@ get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix) strcat(buf, "("); strcat(buf, get_query_def(query, qh)); - strcat(buf, ")"); + strcat(buf, "))"); return pstrdup(buf); } @@ -1694,96 +1740,49 @@ get_attribute_name(Oid relid, int2 attnum) /* ---------- - * check_if_rte_used - Check a targetlist or qual - * if a given rangetable entry - * is used in it + * check_if_rte_used + * Check a targetlist or qual to see if a given rangetable entry + * is used in it * ---------- */ static bool -check_if_rte_used(int rt_index, Node *node, int sup) +check_if_rte_used(Node *node, Index rt_index, int levelsup) +{ + check_if_rte_used_context context; + + context.rt_index = rt_index; + context.levelsup = levelsup; + return check_if_rte_used_walker(node, &context); +} + +static bool +check_if_rte_used_walker(Node *node, + check_if_rte_used_context *context) { if (node == NULL) - return FALSE; - - switch (nodeTag(node)) + return false; + if (IsA(node, Var)) { - case T_TargetEntry: - { - TargetEntry *tle = (TargetEntry *) node; + Var *var = (Var *) node; - return check_if_rte_used(rt_index, - (Node *) (tle->expr), sup); - } - break; - - case T_Aggref: - { - Aggref *aggref = (Aggref *) node; - - return check_if_rte_used(rt_index, - (Node *) (aggref->target), sup); - } - break; - - case T_GroupClause: - return FALSE; - break; - - case T_Expr: - { - Expr *expr = (Expr *) node; - - return check_if_rte_used(rt_index, - (Node *) (expr->args), sup); - } - break; - - case T_Var: - { - Var *var = (Var *) node; - - return var->varno == rt_index && var->varlevelsup == sup; - } - break; - - case T_List: - { - List *l; - - foreach(l, (List *) node) - { - if (check_if_rte_used(rt_index, lfirst(l), sup)) - return TRUE; - } - return FALSE; - } - break; - - case T_SubLink: - { - SubLink *sublink = (SubLink *) node; - Query *query = (Query *) sublink->subselect; - - if (check_if_rte_used(rt_index, (Node *) (query->qual), sup + 1)) - return TRUE; - /* why aren't we looking at query->targetlist, havingQual? */ - - if (check_if_rte_used(rt_index, (Node *) (sublink->lefthand), sup)) - return TRUE; - - return FALSE; - } - break; - - case T_Const: - return FALSE; - break; - - default: - elog(ERROR, "get_ruledef of %s: unknown node type %d in check_if_rte_used()", - rulename, nodeTag(node)); - break; + return var->varno == context->rt_index && + var->varlevelsup == context->levelsup; } + if (IsA(node, SubLink)) + { + SubLink *sublink = (SubLink *) node; + Query *query = (Query *) sublink->subselect; - return FALSE; + /* Recurse into subquery; expression_tree_walker will not */ + if (check_if_rte_used((Node *) (query->targetList), + context->rt_index, context->levelsup + 1) || + check_if_rte_used(query->qual, + context->rt_index, context->levelsup + 1) || + check_if_rte_used(query->havingQual, + context->rt_index, context->levelsup + 1)) + return true; + /* fall through to let expression_tree_walker examine lefthand args */ + } + return expression_tree_walker(node, check_if_rte_used_walker, + (void *) context); }