postgresql/src/backend/parser/parse_query.c
Vadim B. Mikheev ad01dd270d If there is no table in RangeTable for colname then
elog (WARN, "attribute %s not found", colname);
1997-05-31 07:10:25 +00:00

804 lines
20 KiB
C

/*-------------------------------------------------------------------------
*
* 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.16 1997/05/31 07:10:25 vadim Exp $
*
*-------------------------------------------------------------------------
*/
#include <ctype.h>
#include <string.h>
#include "postgres.h"
#include "fmgr.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 "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"
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, aclcheck_error_strings[ACLCHECK_NO_CLASS]);
}
/*
* 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;
Oid type_id;
int 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)++,
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 = NULL;
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,
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)
/* 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, NULL);
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
*/
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);
}