/*------------------------------------------------------------------------- * * 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.6 1996/11/04 04:35:42 momjian Exp $ * *------------------------------------------------------------------------- */ #include <ctype.h> #include <string.h> #include "postgres.h" #include "access/heapam.h" #include "utils/tqual.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 "parser/catalog_utils.h" #include "parser/parse_query.h" /* #include "parser/io.h" */ #include "utils/lsyscache.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" #include "nodes/parsenodes.h" #include "nodes/makefuncs.h" 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, TimeRange *timeRange) { 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, ACL_NO_PRIV_WARNING); } /* * Flags - zero or more from archive,inheritance,union,version * or recursive (transitive closure) * [we don't support them all -- ay 9/94 ] */ rte->inh = inh; rte->timeRange = timeRange; /* RelOID */ rte->relid = RelationGetRelationId(relation); rte->archive = false; 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; int type_id, type_len; RangeTblEntry *rte; rte = refnameRangeTableEntry(pstate->p_rtable, refname); if (rte == NULL) rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE, NULL); 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)++, (Oid)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); } TimeQual makeTimeRange(char *datestring1, char *datestring2, int timecode) /* 0 = snapshot , 1 = timerange */ { TimeQual qual; AbsoluteTime t1,t2; switch (timecode) { case 0: if (datestring1 == NULL) { elog(WARN, "MakeTimeRange: bad snapshot arg"); } t1 = nabstimein(datestring1); if (!AbsoluteTimeIsValid(t1)) { elog(WARN, "bad snapshot time: \"%s\"", datestring1); } qual = TimeFormSnapshotTimeQual(t1); break; case 1: if (datestring1 == NULL) { t1 = NOSTART_ABSTIME; } else { t1 = nabstimein(datestring1); if (!AbsoluteTimeIsValid(t1)) { elog(WARN, "bad range start time: \"%s\"", datestring1); } } if (datestring2 == NULL) { t2 = NOEND_ABSTIME; } else { t2 = nabstimein(datestring2); if (!AbsoluteTimeIsValid(t2)) { elog(WARN, "bad range end time: \"%s\"", datestring2); } } qual = TimeFormRangedTimeQual(t1,t2); break; default: elog(WARN, "MakeTimeRange: internal parser error"); } return qual; } 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, int orig_typeId, int 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) { int 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 { /* binary operator */ ltypeId = (ltree==NULL) ? UNKNOWNOID : exprType(ltree); rtypeId = (rtree==NULL) ? UNKNOWNOID : exprType(rtree); temp = oper(opname, ltypeId, rtypeId); 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; } int find_atttype(Oid relid, char *attrname) { int attid, 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, int *type_id) { Var *varnode; int vnum, attid, vartypeid; Relation rd; RangeTblEntry *rte; rte = refnameRangeTableEntry(pstate->p_rtable, refname); if (rte == NULL) rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE, NULL); vnum = refnameRangeTablePosn(pstate->p_rtable, refname); rd = heap_open(rte->relid); attid = nf_varattno(rd, (char *) 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; int reftype; List *upperIndexpr=NIL; List *lowerIndexpr=NIL; typearray = (Oid) 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; int 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, 0, FALSE); return con; } } con = makeConst(typeid(tp), tlen(tp), val, FALSE, tbyval(tp), FALSE); /* not a set */ 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); Assert(lfirst(pstate->p_insert_columns) != NIL); *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 */ void checkTargetTypes(ParseState *pstate, char *target_colname, char *refname, char *colname) { int attrtype_id, attrtype_target, 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); refname = rte->refname; } Assert(refname != NULL && rte != NULL); Assert(rte != NULL); /* if (pstate->p_is_insert && rte == pstate->p_target_rangetblentry) elog(WARN, "%s not available in this context", colname); */ rd = heap_open(rte->relid); Assert(RelationIsValid(rd)); 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); }