/*------------------------------------------------------------------------- * * parse_query.c-- * take an "optimizable" stmt and make the query tree that * the planner requires. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.25 1997/11/24 05:08:27 momjian Exp $ * *------------------------------------------------------------------------- */ #include #include #include "postgres.h" #include "fmgr.h" #include "access/heapam.h" #include "access/tupmacs.h" #include "utils/builtins.h" #include "utils/elog.h" #include "utils/palloc.h" #include "utils/acl.h" /* for ACL_NO_PRIV_WARNING */ #include "utils/rel.h" /* Relation stuff */ #include "utils/syscache.h" #include "catalog/pg_type.h" #include "catalog/pg_operator.h" #include "parser/catalog_utils.h" #include "parser/parse_query.h" #include "utils/lsyscache.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" #include "nodes/parsenodes.h" #include "nodes/makefuncs.h" static void checkTargetTypes(ParseState *pstate, char *target_colname, char *refname, char *colname); Oid *param_type_info; int pfunc_num_args; /* given refname, return a pointer to the range table entry */ RangeTblEntry * refnameRangeTableEntry(List *rtable, char *refname) { List *temp; foreach(temp, rtable) { RangeTblEntry *rte = lfirst(temp); if (!strcmp(rte->refname, refname)) return rte; } return NULL; } /* given refname, return id of variable; position starts with 1 */ int refnameRangeTablePosn(List *rtable, char *refname) { int index; List *temp; index = 1; foreach(temp, rtable) { RangeTblEntry *rte = lfirst(temp); if (!strcmp(rte->refname, refname)) return index; index++; } return (0); } /* * returns range entry if found, else NULL */ RangeTblEntry * colnameRangeTableEntry(ParseState *pstate, char *colname) { List *et; List *rtable; RangeTblEntry *rte_result; if (pstate->p_is_rule) rtable = lnext(lnext(pstate->p_rtable)); else rtable = pstate->p_rtable; rte_result = NULL; foreach(et, rtable) { RangeTblEntry *rte = lfirst(et); /* only entries on outer(non-function?) scope */ if (!rte->inFromCl && rte != pstate->p_target_rangetblentry) continue; if (get_attnum(rte->relid, colname) != InvalidAttrNumber) { if (rte_result != NULL) { if (!pstate->p_is_insert || rte != pstate->p_target_rangetblentry) elog(WARN, "Column %s is ambiguous", colname); } else rte_result = rte; } } return rte_result; } /* * put new entry in pstate p_rtable structure, or return pointer * if pstate null */ RangeTblEntry * addRangeTableEntry(ParseState *pstate, char *relname, char *refname, bool inh, bool inFromCl) { Relation relation; RangeTblEntry *rte = makeNode(RangeTblEntry); if (pstate != NULL && refnameRangeTableEntry(pstate->p_rtable, refname) != NULL) elog(WARN, "Table name %s specified more than once", refname); rte->relname = pstrdup(relname); rte->refname = pstrdup(refname); relation = heap_openr(relname); if (relation == NULL) { elog(WARN, "%s: %s", relname, aclcheck_error_strings[ACLCHECK_NO_CLASS]); } /* * Flags - zero or more from inheritance,union,version or * recursive (transitive closure) [we don't support them all -- ay * 9/94 ] */ rte->inh = inh; /* RelOID */ rte->relid = RelationGetRelationId(relation); rte->inFromCl = inFromCl; /* * close the relation we're done with it for now. */ if (pstate != NULL) pstate->p_rtable = lappend(pstate->p_rtable, rte); heap_close(relation); return rte; } /* * expandAll - * makes a list of attributes * assumes reldesc caching works */ List * expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno) { Relation rdesc; List *te_tail = NIL, *te_head = NIL; Var *varnode; int varattno, maxattrs; Oid type_id; int type_len; RangeTblEntry *rte; rte = refnameRangeTableEntry(pstate->p_rtable, refname); if (rte == NULL) rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE); rdesc = heap_open(rte->relid); if (rdesc == NULL) { elog(WARN, "Unable to expand all -- heap_open failed on %s", rte->refname); return NIL; } maxattrs = RelationGetNumberOfAttributes(rdesc); for (varattno = 0; varattno <= maxattrs - 1; varattno++) { char *attrname; char *resname = NULL; TargetEntry *te = makeNode(TargetEntry); attrname = pstrdup((rdesc->rd_att->attrs[varattno]->attname).data); varnode = (Var *) make_var(pstate, refname, attrname, &type_id); type_len = (int) tlen(get_id_type(type_id)); handleTargetColname(pstate, &resname, refname, attrname); if (resname != NULL) attrname = resname; /* * Even if the elements making up a set are complex, the set * itself is not. */ te->resdom = makeResdom((AttrNumber) (*this_resno)++, type_id, (Size) type_len, attrname, (Index) 0, (Oid) 0, 0); te->expr = (Node *) varnode; if (te_head == NIL) te_head = te_tail = lcons(te, NIL); else te_tail = lappend(te_tail, te); } heap_close(rdesc); return (te_head); } static void disallow_setop(char *op, Type optype, Node *operand) { if (operand == NULL) return; if (nodeTag(operand) == T_Iter) { elog(NOTICE, "An operand to the '%s' operator returns a set of %s,", op, tname(optype)); elog(WARN, "but '%s' takes single values, not sets.", op); } } static Node * make_operand(char *opname, Node *tree, Oid orig_typeId, Oid true_typeId) { Node *result; Type true_type; Datum val; Oid infunc; if (tree != NULL) { result = tree; true_type = get_id_type(true_typeId); disallow_setop(opname, true_type, result); if (true_typeId != orig_typeId) { /* must coerce */ Const *con = (Const *) result; Assert(nodeTag(result) == T_Const); val = (Datum) textout((struct varlena *) con->constvalue); infunc = typeid_get_retinfunc(true_typeId); con = makeNode(Const); con->consttype = true_typeId; con->constlen = tlen(true_type); con->constvalue = (Datum) fmgr(infunc, val, get_typelem(true_typeId), -1 /* for varchar() type */ ); con->constisnull = false; con->constbyval = true; con->constisset = false; result = (Node *) con; } } else { Const *con = makeNode(Const); con->consttype = true_typeId; con->constlen = 0; con->constvalue = (Datum) (struct varlena *) NULL; con->constisnull = true; con->constbyval = true; con->constisset = false; result = (Node *) con; } return result; } Expr * make_op(char *opname, Node *ltree, Node *rtree) { Oid ltypeId, rtypeId; Operator temp; OperatorTupleForm opform; Oper *newop; Node *left, *right; Expr *result; if (rtree == NULL) { /* right operator */ ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree); temp = right_oper(opname, ltypeId); opform = (OperatorTupleForm) GETSTRUCT(temp); left = make_operand(opname, ltree, ltypeId, opform->oprleft); right = NULL; } else if (ltree == NULL) { /* left operator */ rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree); temp = left_oper(opname, rtypeId); opform = (OperatorTupleForm) GETSTRUCT(temp); right = make_operand(opname, rtree, rtypeId, opform->oprright); left = NULL; } else { char *outstr; Oid infunc, outfunc; Type newtype; #define CONVERTABLE_TYPE(t) ( (t) == INT2OID || \ (t) == INT4OID || \ (t) == OIDOID || \ (t) == FLOAT4OID || \ (t) == FLOAT8OID || \ (t) == CASHOID) /* binary operator */ ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree); rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree); /* * convert constant when using a const of a numeric type and a * non-const of another numeric type */ if (CONVERTABLE_TYPE(ltypeId) && nodeTag(ltree) != T_Const && CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const && !((Const *) rtree)->constiscast) { outfunc = typeid_get_retoutfunc(rtypeId); infunc = typeid_get_retinfunc(ltypeId); outstr = (char *) fmgr(outfunc, ((Const *) rtree)->constvalue); ((Const *) rtree)->constvalue = (Datum) fmgr(infunc, outstr); pfree(outstr); ((Const *) rtree)->consttype = rtypeId = ltypeId; newtype = get_id_type(rtypeId); ((Const *) rtree)->constlen = tlen(newtype); ((Const *) rtree)->constbyval = tbyval(newtype); } if (CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const && CONVERTABLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const && !((Const *) ltree)->constiscast) { outfunc = typeid_get_retoutfunc(ltypeId); infunc = typeid_get_retinfunc(rtypeId); outstr = (char *) fmgr(outfunc, ((Const *) ltree)->constvalue); ((Const *) ltree)->constvalue = (Datum) fmgr(infunc, outstr); pfree(outstr); ((Const *) ltree)->consttype = ltypeId = rtypeId; newtype = get_id_type(ltypeId); ((Const *) ltree)->constlen = tlen(newtype); ((Const *) ltree)->constbyval = tbyval(newtype); } temp = oper(opname, ltypeId, rtypeId, false); opform = (OperatorTupleForm) GETSTRUCT(temp); left = make_operand(opname, ltree, ltypeId, opform->oprleft); right = make_operand(opname, rtree, rtypeId, opform->oprright); } newop = makeOper(oprid(temp), /* opno */ InvalidOid,/* opid */ opform->oprresult, /* operator result type */ 0, NULL); result = makeNode(Expr); result->typeOid = opform->oprresult; result->opType = OP_EXPR; result->oper = (Node *) newop; if (!left) { result->args = lcons(right, NIL); } else if (!right) { result->args = lcons(left, NIL); } else { result->args = lcons(left, lcons(right, NIL)); } return result; } Oid find_atttype(Oid relid, char *attrname) { int attid; Oid vartype; Relation rd; rd = heap_open(relid); if (!RelationIsValid(rd)) { rd = heap_openr(tname(get_id_type(relid))); if (!RelationIsValid(rd)) elog(WARN, "cannot compute type of att %s for relid %d", attrname, relid); } attid = nf_varattno(rd, attrname); if (attid == InvalidAttrNumber) elog(WARN, "Invalid attribute %s\n", attrname); vartype = att_typeid(rd, attid); /* * close relation we're done with it now */ heap_close(rd); return (vartype); } Var * make_var(ParseState *pstate, char *refname, char *attrname, Oid *type_id) { Var *varnode; int vnum, attid; Oid vartypeid; Relation rd; RangeTblEntry *rte; rte = refnameRangeTableEntry(pstate->p_rtable, refname); if (rte == NULL) rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE); vnum = refnameRangeTablePosn(pstate->p_rtable, refname); rd = heap_open(rte->relid); attid = nf_varattno(rd, attrname); if (attid == InvalidAttrNumber) elog(WARN, "Invalid attribute %s\n", attrname); vartypeid = att_typeid(rd, attid); varnode = makeVar(vnum, attid, vartypeid, vnum, attid); heap_close(rd); *type_id = vartypeid; return varnode; } /* * make_array_ref() -- Make an array reference node. * * Array references can hang off of arbitrary nested dot (or * function invocation) expressions. This routine takes a * tree generated by ParseFunc() and an array index and * generates a new array reference tree. We do some simple * typechecking to be sure the dereference is valid in the * type system, but we don't do any bounds checking here. * * indirection is a list of A_Indices */ ArrayRef * make_array_ref(Node *expr, List *indirection) { Oid typearray; HeapTuple type_tuple; TypeTupleForm type_struct_array, type_struct_element; ArrayRef *aref; Oid reftype; List *upperIndexpr = NIL; List *lowerIndexpr = NIL; typearray = exprType(expr); type_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(typearray), 0, 0, 0); if (!HeapTupleIsValid(type_tuple)) elog(WARN, "make_array_ref: Cache lookup failed for type %d\n", typearray); /* get the array type struct from the type tuple */ type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple); if (type_struct_array->typelem == InvalidOid) { elog(WARN, "make_array_ref: type %s is not an array", (Name) &(type_struct_array->typname.data[0])); } /* get the type tuple for the element type */ type_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(type_struct_array->typelem), 0, 0, 0); if (!HeapTupleIsValid(type_tuple)) elog(WARN, "make_array_ref: Cache lookup failed for type %d\n", typearray); type_struct_element = (TypeTupleForm) GETSTRUCT(type_tuple); while (indirection != NIL) { A_Indices *ind = lfirst(indirection); if (ind->lidx) { /* * XXX assumes all lower indices non null in this case */ lowerIndexpr = lappend(lowerIndexpr, ind->lidx); } upperIndexpr = lappend(upperIndexpr, ind->uidx); indirection = lnext(indirection); } aref = makeNode(ArrayRef); aref->refattrlength = type_struct_array->typlen; aref->refelemlength = type_struct_element->typlen; aref->refelemtype = type_struct_array->typelem; aref->refelembyval = type_struct_element->typbyval; aref->refupperindexpr = upperIndexpr; aref->reflowerindexpr = lowerIndexpr; aref->refexpr = expr; aref->refassgnexpr = NULL; if (lowerIndexpr == NIL) /* accessing a single array element */ reftype = aref->refelemtype; else /* request to clip a part of the array, the result is another array */ reftype = typearray; /* * we change it to reflect the true type; since the original * refelemtype doesn't seem to get used anywhere. - ay 10/94 */ aref->refelemtype = reftype; return aref; } ArrayRef * make_array_set(Expr *target_expr, List *upperIndexpr, List *lowerIndexpr, Expr *expr) { Oid typearray; HeapTuple type_tuple; TypeTupleForm type_struct_array; TypeTupleForm type_struct_element; ArrayRef *aref; Oid reftype; typearray = exprType((Node *) target_expr); type_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(typearray), 0, 0, 0); if (!HeapTupleIsValid(type_tuple)) elog(WARN, "make_array_ref: Cache lookup failed for type %d\n", typearray); /* get the array type struct from the type tuple */ type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple); if (type_struct_array->typelem == InvalidOid) { elog(WARN, "make_array_ref: type %s is not an array", (Name) &(type_struct_array->typname.data[0])); } /* get the type tuple for the element type */ type_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(type_struct_array->typelem), 0, 0, 0); if (!HeapTupleIsValid(type_tuple)) elog(WARN, "make_array_ref: Cache lookup failed for type %d\n", typearray); type_struct_element = (TypeTupleForm) GETSTRUCT(type_tuple); aref = makeNode(ArrayRef); aref->refattrlength = type_struct_array->typlen; aref->refelemlength = type_struct_element->typlen; aref->refelemtype = type_struct_array->typelem; aref->refelembyval = type_struct_element->typbyval; aref->refupperindexpr = upperIndexpr; aref->reflowerindexpr = lowerIndexpr; aref->refexpr = (Node *) target_expr; aref->refassgnexpr = (Node *) expr; if (lowerIndexpr == NIL) /* accessing a single array element */ reftype = aref->refelemtype; else /* request to set a part of the array, by another array */ reftype = typearray; aref->refelemtype = reftype; return aref; } /* * * make_const - * * - takes a lispvalue, (as returned to the yacc routine by the lexer) * extracts the type, and makes the appropriate type constant * by invoking the (c-callable) lisp routine c-make-const * via the lisp_call() mechanism * * eventually, produces a "const" lisp-struct as per nodedefs.cl */ Const * make_const(Value *value) { Type tp; Datum val; Const *con; switch (nodeTag(value)) { case T_Integer: tp = type("int4"); val = Int32GetDatum(intVal(value)); break; case T_Float: { float64 dummy; tp = type("float8"); dummy = (float64) palloc(sizeof(float64data)); *dummy = floatVal(value); val = Float64GetDatum(dummy); } break; case T_String: tp = type("unknown"); /* unknown for now, will be type * coerced */ val = PointerGetDatum(textin(strVal(value))); break; case T_Null: default: { if (nodeTag(value) != T_Null) elog(NOTICE, "unknown type : %d\n", nodeTag(value)); /* null const */ con = makeConst(0, 0, (Datum) NULL, true, false, false, false); return con; } } con = makeConst(typeid(tp), tlen(tp), val, false, tbyval(tp), false, /* not a set */ false); return (con); } /* * param_type_init() * * keep enough information around fill out the type of param nodes * used in postquel functions */ void param_type_init(Oid *typev, int nargs) { pfunc_num_args = nargs; param_type_info = typev; } Oid param_type(int t) { if ((t > pfunc_num_args) || (t == 0)) return InvalidOid; return param_type_info[t - 1]; } /* * handleTargetColname - * use column names from insert */ void handleTargetColname(ParseState *pstate, char **resname, char *refname, char *colname) { if (pstate->p_is_insert) { if (pstate->p_insert_columns != NIL) { Ident *id = lfirst(pstate->p_insert_columns); *resname = id->name; pstate->p_insert_columns = lnext(pstate->p_insert_columns); } else elog(WARN, "insert: more expressions than target columns"); } if (pstate->p_is_insert || pstate->p_is_update) checkTargetTypes(pstate, *resname, refname, colname); } /* * checkTargetTypes - * checks value and target column types */ static void checkTargetTypes(ParseState *pstate, char *target_colname, char *refname, char *colname) { Oid attrtype_id, attrtype_target; int resdomno_id, resdomno_target; Relation rd; RangeTblEntry *rte; if (target_colname == NULL || colname == NULL) return; if (refname != NULL) rte = refnameRangeTableEntry(pstate->p_rtable, refname); else { rte = colnameRangeTableEntry(pstate, colname); if (rte == (RangeTblEntry *) NULL) elog(WARN, "attribute %s not found", colname); refname = rte->refname; } /* if (pstate->p_is_insert && rte == pstate->p_target_rangetblentry) elog(WARN, "%s not available in this context", colname); */ rd = heap_open(rte->relid); resdomno_id = varattno(rd, colname); attrtype_id = att_typeid(rd, resdomno_id); resdomno_target = varattno(pstate->p_target_relation, target_colname); attrtype_target = att_typeid(pstate->p_target_relation, resdomno_target); if (attrtype_id != attrtype_target) elog(WARN, "Type of %s does not match target column %s", colname, target_colname); if ((attrtype_id == BPCHAROID || attrtype_id == VARCHAROID) && rd->rd_att->attrs[resdomno_id - 1]->attlen != pstate->p_target_relation->rd_att->attrs[resdomno_target - 1]->attlen) elog(WARN, "Length of %s does not match length of target column %s", colname, target_colname); heap_close(rd); }