postgresql/src/backend/parser/analyze.c

2767 lines
66 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* analyze.c--
* transform the parse tree into a query tree
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
1997-09-18 22:22:58 +02:00
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.44 1997/09/18 20:20:58 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "postgres.h"
#include "nodes/nodes.h"
#include "nodes/params.h"
#include "nodes/primnodes.h"
#include "nodes/parsenodes.h"
#include "nodes/relation.h"
#include "parse.h" /* for AND, OR, etc. */
#include "catalog/pg_type.h" /* for INT4OID, etc. */
#include "catalog/pg_proc.h"
#include "utils/elog.h"
#include "utils/builtins.h" /* namecmp(), textout() */
#include "utils/lsyscache.h"
#include "utils/palloc.h"
#include "utils/mcxt.h"
#include "utils/syscache.h"
#include "utils/acl.h"
#include "parser/parse_query.h"
#include "parser/parse_state.h"
#include "nodes/makefuncs.h" /* for makeResdom(), etc. */
#include "nodes/nodeFuncs.h"
#include "commands/sequence.h"
#include "optimizer/clauses.h"
#include "access/heapam.h"
#include "miscadmin.h"
#include "port-protos.h" /* strdup() */
/* convert the parse tree into a query tree */
static Query *transformStmt(ParseState *pstate, Node *stmt);
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
static Query *transformInsertStmt(ParseState *pstate, AppendStmt *stmt);
static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
static Query *transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt);
static Query *transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt);
static Query *transformCursorStmt(ParseState *pstate, CursorStmt *stmt);
static Node *handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno);
#define EXPR_COLUMN_FIRST 1
#define EXPR_RELATION_FIRST 2
static Node *transformExpr(ParseState *pstate, Node *expr, int precedence);
static Node *transformIdent(ParseState *pstate, Node *expr, int precedence);
static void makeRangeTable(ParseState *pstate, char *relname, List *frmList);
static List *expandAllTables(ParseState *pstate);
static char *figureColname(Node *expr, Node *resval);
static List *makeTargetNames(ParseState *pstate, List *cols);
static List *transformTargetList(ParseState *pstate, List *targetlist);
static TargetEntry *
make_targetlist_expr(ParseState *pstate,
char *colname, Node *expr,
List *arrayRef);
static bool inWhereClause = false;
static Node *transformWhereClause(ParseState *pstate, Node *a_expr);
static List *
transformGroupClause(ParseState *pstate, List *grouplist,
List *targetlist);
static List *
transformSortClause(ParseState *pstate,
List *orderlist, List *targetlist,
char *uniqueFlag);
static void parseFromClause(ParseState *pstate, List *frmList);
static Node *
ParseFunc(ParseState *pstate, char *funcname,
List *fargs, int *curr_resno);
static List *setup_tlist(char *attname, Oid relid);
static List *setup_base_tlist(Oid typeid);
static void
make_arguments(int nargs, List *fargs, Oid *input_typeids,
Oid *function_typeids);
static void AddAggToParseState(ParseState *pstate, Aggreg *aggreg);
static void finalizeAggregates(ParseState *pstate, Query *qry);
static void parseCheckAggregates(ParseState *pstate, Query *qry);
static ParseState *makeParseState(void);
/*****************************************************************************
*
*****************************************************************************/
/*
* makeParseState() --
* allocate and initialize a new ParseState.
* the CALLERS is responsible for freeing the ParseState* returned
*
*/
static ParseState *
makeParseState(void)
{
ParseState *pstate;
pstate = malloc(sizeof(ParseState));
pstate->p_last_resno = 1;
pstate->p_rtable = NIL;
pstate->p_numAgg = 0;
pstate->p_aggs = NIL;
pstate->p_is_insert = false;
pstate->p_insert_columns = NIL;
pstate->p_is_update = false;
pstate->p_is_rule = false;
pstate->p_target_relation = NULL;
pstate->p_target_rangetblentry = NULL;
return (pstate);
}
1996-10-30 03:02:41 +01:00
/*
* parse_analyze -
* analyze a list of parse trees and transform them if necessary.
*
* Returns a list of transformed parse trees. Optimizable statements are
* all transformed to Query while the rest stays the same.
*
* CALLER is responsible for freeing the QueryTreeList* returned
*/
QueryTreeList *
parse_analyze(List *pl)
{
QueryTreeList *result;
ParseState *pstate;
int i = 0;
result = malloc(sizeof(QueryTreeList));
result->len = length(pl);
result->qtrees = (Query **) malloc(result->len * sizeof(Query *));
inWhereClause = false; /* to avoid nextval(sequence) in WHERE */
while (pl != NIL)
{
pstate = makeParseState();
result->qtrees[i++] = transformStmt(pstate, lfirst(pl));
pl = lnext(pl);
if (pstate->p_target_relation != NULL)
heap_close(pstate->p_target_relation);
free(pstate);
}
return result;
}
/*
* transformStmt -
* transform a Parse tree. If it is an optimizable statement, turn it
* into a Query tree.
*/
static Query *
transformStmt(ParseState *pstate, Node *parseTree)
{
Query *result = NULL;
switch (nodeTag(parseTree))
{
/*------------------------
* Non-optimizable statements
*------------------------
*/
case T_IndexStmt:
result = transformIndexStmt(pstate, (IndexStmt *) parseTree);
break;
case T_ExtendStmt:
result = transformExtendStmt(pstate, (ExtendStmt *) parseTree);
break;
case T_RuleStmt:
result = transformRuleStmt(pstate, (RuleStmt *) parseTree);
break;
case T_ViewStmt:
{
ViewStmt *n = (ViewStmt *) parseTree;
n->query = (Query *) transformStmt(pstate, (Node *) n->query);
result = makeNode(Query);
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) n;
}
break;
case T_VacuumStmt:
{
MemoryContext oldcontext;
/*
* make sure that this Query is allocated in TopMemory
* context because vacuum spans transactions and we don't
* want to lose the vacuum Query due to end-of-transaction
* free'ing
*/
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
result = makeNode(Query);
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) parseTree;
MemoryContextSwitchTo(oldcontext);
break;
}
case T_ExplainStmt:
{
ExplainStmt *n = (ExplainStmt *) parseTree;
result = makeNode(Query);
result->commandType = CMD_UTILITY;
n->query = transformStmt(pstate, (Node *) n->query);
result->utilityStmt = (Node *) parseTree;
}
break;
/*------------------------
* Optimizable statements
*------------------------
*/
case T_AppendStmt:
result = transformInsertStmt(pstate, (AppendStmt *) parseTree);
break;
case T_DeleteStmt:
result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree);
break;
case T_ReplaceStmt:
result = transformUpdateStmt(pstate, (ReplaceStmt *) parseTree);
break;
case T_CursorStmt:
result = transformCursorStmt(pstate, (CursorStmt *) parseTree);
break;
case T_RetrieveStmt:
result = transformSelectStmt(pstate, (RetrieveStmt *) parseTree);
break;
default:
/*
* other statments don't require any transformation-- just
* return the original parsetree
*/
result = makeNode(Query);
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) parseTree;
break;
}
return result;
}
/*
* transformDeleteStmt -
* transforms a Delete Statement
*/
static Query *
transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
{
Query *qry = makeNode(Query);
qry->commandType = CMD_DELETE;
/* set up a range table */
makeRangeTable(pstate, stmt->relname, NULL);
qry->uniqueFlag = NULL;
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause);
qry->rtable = pstate->p_rtable;
qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname);
/* make sure we don't have aggregates in the where clause */
if (pstate->p_numAgg > 0)
parseCheckAggregates(pstate, qry);
return (Query *) qry;
}
/*
* transformInsertStmt -
* transform an Insert Statement
*/
static Query *
transformInsertStmt(ParseState *pstate, AppendStmt *stmt)
{
Query *qry = makeNode(Query); /* make a new query tree */
qry->commandType = CMD_INSERT;
pstate->p_is_insert = true;
/* set up a range table */
makeRangeTable(pstate, stmt->relname, stmt->fromClause);
qry->uniqueFlag = NULL;
/* fix the target list */
pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols);
1996-10-30 03:02:41 +01:00
qry->targetList = transformTargetList(pstate, stmt->targetList);
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause);
/* now the range table will not change */
qry->rtable = pstate->p_rtable;
qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname);
if (pstate->p_numAgg > 0)
finalizeAggregates(pstate, qry);
return (Query *) qry;
}
/*
* transformIndexStmt -
* transforms the qualification of the index statement
*/
static Query *
transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
{
Query *q;
q = makeNode(Query);
q->commandType = CMD_UTILITY;
/* take care of the where clause */
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
stmt->rangetable = pstate->p_rtable;
q->utilityStmt = (Node *) stmt;
return q;
}
/*
* transformExtendStmt -
* transform the qualifications of the Extend Index Statement
*
*/
static Query *
transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
{
Query *q;
q = makeNode(Query);
q->commandType = CMD_UTILITY;
/* take care of the where clause */
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
stmt->rangetable = pstate->p_rtable;
q->utilityStmt = (Node *) stmt;
return q;
}
/*
* transformRuleStmt -
* transform a Create Rule Statement. The actions is a list of parse
* trees which is transformed into a list of query trees.
*/
static Query *
transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
{
Query *q;
List *actions;
q = makeNode(Query);
q->commandType = CMD_UTILITY;
actions = stmt->actions;
/*
* transform each statment, like parse_analyze()
*/
while (actions != NIL)
{
/*
* NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW'
* equal to 2.
*/
addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
FALSE, FALSE, NULL);
addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
FALSE, FALSE, NULL);
pstate->p_last_resno = 1;
pstate->p_is_rule = true; /* for expand all */
pstate->p_numAgg = 0;
pstate->p_aggs = NULL;
lfirst(actions) = transformStmt(pstate, lfirst(actions));
actions = lnext(actions);
}
/* take care of the where clause */
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
q->utilityStmt = (Node *) stmt;
return q;
}
/*
* transformSelectStmt -
* transforms a Select Statement
*
*/
static Query *
transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt)
{
Query *qry = makeNode(Query);
qry->commandType = CMD_SELECT;
/* set up a range table */
makeRangeTable(pstate, NULL, stmt->fromClause);
qry->uniqueFlag = stmt->unique;
qry->into = stmt->into;
qry->isPortal = FALSE;
/* fix the target list */
qry->targetList = transformTargetList(pstate, stmt->targetList);
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause);
/* check subselect clause */
if (stmt->selectClause)
elog(NOTICE, "UNION not yet supported; using first SELECT only", NULL);
/* check subselect clause */
if (stmt->havingClause)
elog(NOTICE, "HAVING not yet supported; ignore clause", NULL);
/* fix order clause */
qry->sortClause = transformSortClause(pstate,
stmt->sortClause,
qry->targetList,
qry->uniqueFlag);
/* fix group by clause */
qry->groupClause = transformGroupClause(pstate,
stmt->groupClause,
qry->targetList);
qry->rtable = pstate->p_rtable;
if (pstate->p_numAgg > 0)
finalizeAggregates(pstate, qry);
return (Query *) qry;
}
/*
* transformUpdateStmt -
* transforms an update statement
*
*/
static Query *
transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt)
{
Query *qry = makeNode(Query);
qry->commandType = CMD_UPDATE;
pstate->p_is_update = true;
/*
* the FROM clause is non-standard SQL syntax. We used to be able to
* do this with REPLACE in POSTQUEL so we keep the feature.
*/
makeRangeTable(pstate, stmt->relname, stmt->fromClause);
/* fix the target list */
qry->targetList = transformTargetList(pstate, stmt->targetList);
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause);
qry->rtable = pstate->p_rtable;
qry->resultRelation = refnameRangeTablePosn(pstate->p_rtable, stmt->relname);
/* make sure we don't have aggregates in the where clause */
if (pstate->p_numAgg > 0)
parseCheckAggregates(pstate, qry);
return (Query *) qry;
}
/*
* transformCursorStmt -
* transform a Create Cursor Statement
*
*/
static Query *
transformCursorStmt(ParseState *pstate, CursorStmt *stmt)
{
Query *qry = makeNode(Query);
/*
* in the old days, a cursor statement is a 'retrieve into portal'; If
* you change the following, make sure you also go through the code in
* various places that tests the kind of operation.
*/
qry->commandType = CMD_SELECT;
/* set up a range table */
makeRangeTable(pstate, NULL, stmt->fromClause);
qry->uniqueFlag = stmt->unique;
qry->into = stmt->portalname;
qry->isPortal = TRUE;
qry->isBinary = stmt->binary; /* internal portal */
/* fix the target list */
qry->targetList = transformTargetList(pstate, stmt->targetList);
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause);
/* fix order clause */
qry->sortClause = transformSortClause(pstate,
stmt->sortClause,
qry->targetList,
qry->uniqueFlag);
/* fix group by clause */
qry->groupClause = transformGroupClause(pstate,
stmt->groupClause,
qry->targetList);
qry->rtable = pstate->p_rtable;
if (pstate->p_numAgg > 0)
finalizeAggregates(pstate, qry);
return (Query *) qry;
}
/*****************************************************************************
*
* Transform Exprs, Aggs, etc.
*
*****************************************************************************/
/*
* transformExpr -
* analyze and transform expressions. Type checking and type casting is
* done here. The optimizer and the executor cannot handle the original
* (raw) expressions collected by the parse tree. Hence the transformation
* here.
*/
static Node *
transformExpr(ParseState *pstate, Node *expr, int precedence)
{
Node *result = NULL;
if (expr == NULL)
return NULL;
switch (nodeTag(expr))
{
case T_Attr:
{
Attr *att = (Attr *) expr;
Node *temp;
/* what if att.attrs == "*"?? */
temp = handleNestedDots(pstate, att, &pstate->p_last_resno);
if (att->indirection != NIL)
{
List *idx = att->indirection;
while (idx != NIL)
{
A_Indices *ai = (A_Indices *) lfirst(idx);
Node *lexpr = NULL,
*uexpr;
uexpr = transformExpr(pstate, ai->uidx, precedence); /* must exists */
if (exprType(uexpr) != INT4OID)
elog(WARN, "array index expressions must be int4's");
if (ai->lidx != NULL)
{
lexpr = transformExpr(pstate, ai->lidx, precedence);
if (exprType(lexpr) != INT4OID)
elog(WARN, "array index expressions must be int4's");
}
#if 0
pfree(ai->uidx);
if (ai->lidx != NULL)
pfree(ai->lidx);
#endif
ai->lidx = lexpr;
ai->uidx = uexpr;
/*
* note we reuse the list of indices, make sure we
* don't free them! Otherwise, make a new list
* here
*/
idx = lnext(idx);
}
result = (Node *) make_array_ref(temp, att->indirection);
}
else
{
result = temp;
}
break;
}
case T_A_Const:
{
A_Const *con = (A_Const *) expr;
Value *val = &con->val;
if (con->typename != NULL)
{
result = parser_typecast(val, con->typename, -1);
}
else
{
result = (Node *) make_const(val);
}
break;
}
case T_ParamNo:
{
ParamNo *pno = (ParamNo *) expr;
Oid toid;
int paramno;
Param *param;
paramno = pno->number;
toid = param_type(paramno);
if (!OidIsValid(toid))
{
elog(WARN, "Parameter '$%d' is out of range",
paramno);
}
param = makeNode(Param);
param->paramkind = PARAM_NUM;
param->paramid = (AttrNumber) paramno;
param->paramname = "<unnamed>";
param->paramtype = (Oid) toid;
param->param_tlist = (List *) NULL;
result = (Node *) param;
break;
}
case T_A_Expr:
{
A_Expr *a = (A_Expr *) expr;
switch (a->oper)
{
case OP:
{
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
result = (Node *) make_op(a->opname, lexpr, rexpr);
}
break;
case ISNULL:
{
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
result = ParseFunc(pstate,
"nullvalue", lcons(lexpr, NIL),
&pstate->p_last_resno);
}
break;
case NOTNULL:
{
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
result = ParseFunc(pstate,
"nonnullvalue", lcons(lexpr, NIL),
&pstate->p_last_resno);
}
break;
case AND:
{
Expr *expr = makeNode(Expr);
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
if (exprType(lexpr) != BOOLOID)
elog(WARN,
"left-hand side of AND is type '%s', not bool",
tname(get_id_type(exprType(lexpr))));
if (exprType(rexpr) != BOOLOID)
elog(WARN,
"right-hand side of AND is type '%s', not bool",
tname(get_id_type(exprType(rexpr))));
expr->typeOid = BOOLOID;
expr->opType = AND_EXPR;
expr->args = makeList(lexpr, rexpr, -1);
result = (Node *) expr;
}
break;
case OR:
{
Expr *expr = makeNode(Expr);
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
if (exprType(lexpr) != BOOLOID)
elog(WARN,
"left-hand side of OR is type '%s', not bool",
tname(get_id_type(exprType(lexpr))));
if (exprType(rexpr) != BOOLOID)
elog(WARN,
"right-hand side of OR is type '%s', not bool",
tname(get_id_type(exprType(rexpr))));
expr->typeOid = BOOLOID;
expr->opType = OR_EXPR;
expr->args = makeList(lexpr, rexpr, -1);
result = (Node *) expr;
}
break;
case NOT:
{
Expr *expr = makeNode(Expr);
Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
if (exprType(rexpr) != BOOLOID)
elog(WARN,
"argument to NOT is type '%s', not bool",
tname(get_id_type(exprType(rexpr))));
expr->typeOid = BOOLOID;
expr->opType = NOT_EXPR;
expr->args = makeList(rexpr, -1);
result = (Node *) expr;
}
break;
}
break;
}
case T_Ident:
{
/*
* look for a column name or a relation name (the default
* behavior)
*/
result = transformIdent(pstate, expr, precedence);
break;
}
case T_FuncCall:
{
FuncCall *fn = (FuncCall *) expr;
List *args;
/* transform the list of arguments */
foreach(args, fn->args)
lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence);
result = ParseFunc(pstate,
fn->funcname, fn->args, &pstate->p_last_resno);
break;
}
default:
/* should not reach here */
elog(WARN, "transformExpr: does not know how to transform %d\n",
nodeTag(expr));
break;
}
return result;
}
static Node *
transformIdent(ParseState *pstate, Node *expr, int precedence)
{
Ident *ident = (Ident *) expr;
RangeTblEntry *rte;
Node *column_result,
*relation_result,
*result;
column_result = relation_result = result = 0;
/* try to find the ident as a column */
if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL)
{
Attr *att = makeNode(Attr);
att->relname = rte->refname;
att->attrs = lcons(makeString(ident->name), NIL);
column_result =
(Node *) handleNestedDots(pstate, att, &pstate->p_last_resno);
}
/* try to find the ident as a relation */
if (refnameRangeTableEntry(pstate->p_rtable, ident->name) != NULL)
{
ident->isRel = TRUE;
relation_result = (Node *) ident;
}
/* choose the right result based on the precedence */
if (precedence == EXPR_COLUMN_FIRST)
{
if (column_result)
result = column_result;
else
result = relation_result;
}
else
{
if (relation_result)
result = relation_result;
else
result = column_result;
}
if (result == NULL)
elog(WARN, "attribute \"%s\" not found", ident->name);
return result;
}
/*****************************************************************************
*
* From Clause
*
*****************************************************************************/
/*
* parseFromClause -
* turns the table references specified in the from-clause into a
* range table. The range table may grow as we transform the expressions
* in the target list. (Note that this happens because in POSTQUEL, we
* allow references to relations not specified in the from-clause. We
* also allow that in our POST-SQL)
*
*/
static void
parseFromClause(ParseState *pstate, List *frmList)
{
List *fl;
foreach(fl, frmList)
{
RangeVar *r = lfirst(fl);
RelExpr *baserel = r->relExpr;
char *relname = baserel->relname;
char *refname = r->name;
RangeTblEntry *rte;
if (refname == NULL)
refname = relname;
/*
* marks this entry to indicate it comes from the FROM clause. In
* SQL, the target list can only refer to range variables
* specified in the from clause but we follow the more powerful
* POSTQUEL semantics and automatically generate the range
* variable if not specified. However there are times we need to
* know whether the entries are legitimate.
*
* eg. select * from foo f where f.x = 1; will generate wrong answer
* if we expand * to foo.x.
*/
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE,
baserel->timeRange);
}
}
/*
* makeRangeTable -
* make a range table with the specified relation (optional) and the
* from-clause.
*/
static void
makeRangeTable(ParseState *pstate, char *relname, List *frmList)
{
RangeTblEntry *rte;
parseFromClause(pstate, frmList);
if (relname == NULL)
return;
if (refnameRangeTablePosn(pstate->p_rtable, relname) < 1)
rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE, NULL);
else
rte = refnameRangeTableEntry(pstate->p_rtable, relname);
pstate->p_target_rangetblentry = rte;
Assert(pstate->p_target_relation == NULL);
pstate->p_target_relation = heap_open(rte->relid);
Assert(pstate->p_target_relation != NULL);
1996-10-30 03:02:41 +01:00
/* will close relation later */
}
/*
* exprType -
* returns the Oid of the type of the expression. (Used for typechecking.)
*/
Oid
exprType(Node *expr)
{
Oid type = (Oid) 0;
switch (nodeTag(expr))
{
case T_Func:
type = ((Func *) expr)->functype;
break;
case T_Iter:
type = ((Iter *) expr)->itertype;
break;
case T_Var:
type = ((Var *) expr)->vartype;
break;
case T_Expr:
type = ((Expr *) expr)->typeOid;
break;
case T_Const:
type = ((Const *) expr)->consttype;
break;
case T_ArrayRef:
type = ((ArrayRef *) expr)->refelemtype;
break;
case T_Aggreg:
type = ((Aggreg *) expr)->aggtype;
break;
case T_Param:
type = ((Param *) expr)->paramtype;
break;
case T_Ident:
/* is this right? */
type = UNKNOWNOID;
break;
default:
elog(WARN, "exprType: don't know how to get type for %d node",
nodeTag(expr));
break;
}
return type;
}
/*
* expandAllTables -
* turns '*' (in the target list) into a list of attributes (of all
* relations in the range table)
*/
static List *
expandAllTables(ParseState *pstate)
{
List *target = NIL;
List *legit_rtable = NIL;
List *rt,
*rtable;
rtable = pstate->p_rtable;
if (pstate->p_is_rule)
{
/*
* skip first two entries, "*new*" and "*current*"
*/
rtable = lnext(lnext(pstate->p_rtable));
}
/* this should not happen */
if (rtable == NULL)
elog(WARN, "cannot expand: null p_rtable");
/*
* go through the range table and make a list of range table entries
* which we will expand.
*/
foreach(rt, rtable)
{
RangeTblEntry *rte = lfirst(rt);
/*
* we only expand those specify in the from clause. (This will
* also prevent us from using the wrong table in inserts: eg.
* tenk2 in "insert into tenk2 select * from tenk1;")
*/
if (!rte->inFromCl)
continue;
legit_rtable = lappend(legit_rtable, rte);
}
foreach(rt, legit_rtable)
{
RangeTblEntry *rte = lfirst(rt);
List *temp = target;
if (temp == NIL)
target = expandAll(pstate, rte->relname, rte->refname,
&pstate->p_last_resno);
else
{
while (temp != NIL && lnext(temp) != NIL)
temp = lnext(temp);
lnext(temp) = expandAll(pstate, rte->relname, rte->refname,
&pstate->p_last_resno);
}
}
return target;
}
/*
* figureColname -
* if the name of the resulting column is not specified in the target
* list, we have to guess.
*
*/
static char *
figureColname(Node *expr, Node *resval)
{
switch (nodeTag(expr))
{
case T_Aggreg:
return (char *) /* XXX */
((Aggreg *) expr)->aggname;
case T_Expr:
if (((Expr *) expr)->opType == FUNC_EXPR)
{
if (nodeTag(resval) == T_FuncCall)
return ((FuncCall *) resval)->funcname;
}
break;
default:
break;
}
return "?column?";
}
/*****************************************************************************
*
* Target list
*
*****************************************************************************/
/*
1996-10-30 03:02:41 +01:00
* makeTargetNames -
* generate a list of column names if not supplied or
* test supplied column names to make sure they are in target table
* (used exclusively for inserts)
*/
static List *
makeTargetNames(ParseState *pstate, List *cols)
{
List *tl = NULL;
/* Generate ResTarget if not supplied */
if (cols == NIL)
{
int numcol;
int i;
AttributeTupleForm *attr = pstate->p_target_relation->rd_att->attrs;
numcol = pstate->p_target_relation->rd_rel->relnatts;
for (i = 0; i < numcol; i++)
{
Ident *id = makeNode(Ident);
id->name = palloc(NAMEDATALEN);
strNcpy(id->name, attr[i]->attname.data, NAMEDATALEN - 1);
id->indirection = NIL;
id->isRel = false;
if (tl == NIL)
cols = tl = lcons(id, NIL);
else
{
lnext(tl) = lcons(id, NIL);
tl = lnext(tl);
}
}
}
else
foreach(tl, cols)
/* elog on failure */
varattno(pstate->p_target_relation, ((Ident *) lfirst(tl))->name);
1996-10-30 03:02:41 +01:00
return cols;
}
/*
* transformTargetList -
* turns a list of ResTarget's into a list of TargetEntry's
*/
static List *
transformTargetList(ParseState *pstate, List *targetlist)
{
List *p_target = NIL;
List *tail_p_target = NIL;
while (targetlist != NIL)
{
ResTarget *res = (ResTarget *) lfirst(targetlist);
TargetEntry *tent = makeNode(TargetEntry);
switch (nodeTag(res->val))
{
case T_Ident:
{
Node *expr;
Oid type_id;
int type_len;
char *identname;
char *resname;
identname = ((Ident *) res->val)->name;
handleTargetColname(pstate, &res->name, NULL, identname);
/*
* here we want to look for column names only, not
* relation
*/
/*
* names (even though they can be stored in Ident
* nodes,
*/
/* too) */
expr = transformIdent(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
type_id = exprType(expr);
type_len = tlen(get_id_type(type_id));
resname = (res->name) ? res->name : identname;
tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
(Oid) type_id,
(Size) type_len,
resname,
(Index) 0,
(Oid) 0,
0);
tent->expr = expr;
break;
}
case T_ParamNo:
case T_FuncCall:
case T_A_Const:
case T_A_Expr:
{
Node *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
handleTargetColname(pstate, &res->name, NULL, NULL);
/* note indirection has not been transformed */
if (pstate->p_is_insert && res->indirection != NIL)
{
/* this is an array assignment */
char *val;
char *str,
*save_str;
List *elt;
int i = 0,
ndims;
int lindx[MAXDIM],
uindx[MAXDIM];
int resdomno;
Relation rd;
Value *constval;
if (exprType(expr) != UNKNOWNOID ||
!IsA(expr, Const))
elog(WARN, "yyparse: string constant expected");
val = (char *) textout((struct varlena *)
((Const *) expr)->constvalue);
str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2);
foreach(elt, res->indirection)
{
A_Indices *aind = (A_Indices *) lfirst(elt);
aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
if (!IsA(aind->uidx, Const))
elog(WARN,
"Array Index for Append should be a constant");
uindx[i] = ((Const *) aind->uidx)->constvalue;
if (aind->lidx != NULL)
{
aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
if (!IsA(aind->lidx, Const))
elog(WARN,
"Array Index for Append should be a constant");
lindx[i] = ((Const *) aind->lidx)->constvalue;
}
else
{
lindx[i] = 1;
}
if (lindx[i] > uindx[i])
elog(WARN, "yyparse: lower index cannot be greater than upper index");
sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
str += strlen(str);
i++;
}
sprintf(str, "=%s", val);
rd = pstate->p_target_relation;
Assert(rd != NULL);
resdomno = varattno(rd, res->name);
ndims = att_attnelems(rd, resdomno);
if (i != ndims)
elog(WARN, "yyparse: array dimensions do not match");
constval = makeNode(Value);
constval->type = T_String;
constval->val.str = save_str;
tent = make_targetlist_expr(pstate, res->name,
(Node *) make_const(constval),
NULL);
pfree(save_str);
}
else
{
char *colname = res->name;
/* this is not an array assignment */
if (colname == NULL)
{
/*
* if you're wondering why this is here, look
* at the yacc grammar for why a name can be
* missing. -ay
*/
colname = figureColname(expr, res->val);
}
if (res->indirection)
{
List *ilist = res->indirection;
while (ilist != NIL)
{
A_Indices *ind = lfirst(ilist);
ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
ilist = lnext(ilist);
}
}
res->name = colname;
tent = make_targetlist_expr(pstate, res->name, expr,
res->indirection);
}
break;
}
case T_Attr:
{
Oid type_id;
int type_len;
Attr *att = (Attr *) res->val;
Node *result;
char *attrname;
char *resname;
Resdom *resnode;
List *attrs = att->attrs;
/*
* Target item is a single '*', expand all tables (eg.
* SELECT * FROM emp)
*/
if (att->relname != NULL && !strcmp(att->relname, "*"))
{
if (tail_p_target == NIL)
p_target = tail_p_target = expandAllTables(pstate);
else
lnext(tail_p_target) = expandAllTables(pstate);
while (lnext(tail_p_target) != NIL)
/* make sure we point to the last target entry */
tail_p_target = lnext(tail_p_target);
/*
* skip rest of while loop
*/
targetlist = lnext(targetlist);
continue;
}
/*
* Target item is relation.*, expand the table (eg.
* SELECT emp.*, dname FROM emp, dept)
*/
attrname = strVal(lfirst(att->attrs));
if (att->attrs != NIL && !strcmp(attrname, "*"))
{
/*
* tail_p_target is the target list we're building
* in the while loop. Make sure we fix it after
* appending more nodes.
*/
if (tail_p_target == NIL)
p_target = tail_p_target = expandAll(pstate, att->relname,
att->relname, &pstate->p_last_resno);
else
lnext(tail_p_target) =
expandAll(pstate, att->relname, att->relname,
&pstate->p_last_resno);
while (lnext(tail_p_target) != NIL)
/* make sure we point to the last target entry */
tail_p_target = lnext(tail_p_target);
/*
* skip the rest of the while loop
*/
targetlist = lnext(targetlist);
continue;
}
/*
* Target item is fully specified: ie.
* relation.attribute
*/
result = handleNestedDots(pstate, att, &pstate->p_last_resno);
handleTargetColname(pstate, &res->name, att->relname, attrname);
if (att->indirection != NIL)
{
List *ilist = att->indirection;
while (ilist != NIL)
{
A_Indices *ind = lfirst(ilist);
ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
ilist = lnext(ilist);
}
result = (Node *) make_array_ref(result, att->indirection);
}
type_id = exprType(result);
type_len = tlen(get_id_type(type_id));
/* move to last entry */
while (lnext(attrs) != NIL)
attrs = lnext(attrs);
resname = (res->name) ? res->name : strVal(lfirst(attrs));
resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
(Oid) type_id,
(Size) type_len,
resname,
(Index) 0,
(Oid) 0,
0);
tent->resdom = resnode;
tent->expr = result;
break;
}
default:
/* internal error */
elog(WARN,
"internal error: do not know how to transform targetlist");
break;
}
if (p_target == NIL)
{
p_target = tail_p_target = lcons(tent, NIL);
}
else
{
lnext(tail_p_target) = lcons(tent, NIL);
tail_p_target = lnext(tail_p_target);
}
targetlist = lnext(targetlist);
}
1996-10-30 03:02:41 +01:00
return p_target;
}
/*
* make_targetlist_expr -
* make a TargetEntry from an expression
*
* arrayRef is a list of transformed A_Indices
*/
static TargetEntry *
make_targetlist_expr(ParseState *pstate,
char *colname,
Node *expr,
List *arrayRef)
{
Oid type_id,
attrtype;
int type_len,
attrlen;
int resdomno;
Relation rd;
bool attrisset;
TargetEntry *tent;
Resdom *resnode;
if (expr == NULL)
elog(WARN, "make_targetlist_expr: invalid use of NULL expression");
type_id = exprType(expr);
if (type_id == InvalidOid)
{
type_len = 0;
}
else
type_len = tlen(get_id_type(type_id));
/* I have no idea what the following does! */
/* It appears to process target columns that will be receiving results */
if (pstate->p_is_insert || pstate->p_is_update)
{
/*
* append or replace query -- append, replace work only on one
* relation, so multiple occurence of same resdomno is bogus
*/
rd = pstate->p_target_relation;
Assert(rd != NULL);
resdomno = varattno(rd, colname);
attrisset = varisset(rd, colname);
attrtype = att_typeid(rd, resdomno);
if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
attrtype = GetArrayElementType(attrtype);
if (attrtype == BPCHAROID || attrtype == VARCHAROID)
{
attrlen = rd->rd_att->attrs[resdomno - 1]->attlen;
}
else
{
attrlen = tlen(get_id_type(attrtype));
}
#if 0
if (Input_is_string && Typecast_ok)
{
Datum val;
if (type_id == typeid(type("unknown")))
{
val = (Datum) textout((struct varlena *)
((Const) lnext(expr))->constvalue);
}
else
{
val = ((Const) lnext(expr))->constvalue;
}
if (attrisset)
{
lnext(expr) = makeConst(attrtype,
attrlen,
val,
false,
true,
true, /* is set */
false);
}
else
{
lnext(expr) =
makeConst(attrtype,
attrlen,
(Datum) fmgr(typeid_get_retinfunc(attrtype),
val, get_typelem(attrtype), -1),
false,
true /* Maybe correct-- 80% chance */ ,
false, /* is not a set */
false);
}
}
else if ((Typecast_ok) && (attrtype != type_id))
{
lnext(expr) =
parser_typecast2(expr, get_id_type(attrtype));
}
else if (attrtype != type_id)
{
if ((attrtype == INT2OID) && (type_id == INT4OID))
lfirst(expr) = lispInteger(INT2OID); /* handle CASHOID too */
else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID))
lfirst(expr) = lispInteger(FLOAT4OID);
else
elog(WARN, "unequal type in tlist : %s \n", colname);
}
Input_is_string = false;
Input_is_integer = false;
Typecast_ok = true;
#endif
1996-10-30 03:02:41 +01:00
if (attrtype != type_id)
{
if (IsA(expr, Const))
{
/* try to cast the constant */
if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
{
/* updating a single item */
Oid typelem = get_typelem(attrtype);
expr = (Node *) parser_typecast2(expr,
type_id,
get_id_type(typelem),
attrlen);
}
else
expr = (Node *) parser_typecast2(expr,
type_id,
get_id_type(attrtype),
attrlen);
}
else
{
/* currently, we can't handle casting of expressions */
elog(WARN, "parser: attribute '%s' is of type '%s' but expression is of type '%s'",
colname,
get_id_typname(attrtype),
get_id_typname(type_id));
}
}
if (arrayRef != NIL)
{
Expr *target_expr;
Attr *att = makeNode(Attr);
List *ar = arrayRef;
List *upperIndexpr = NIL;
List *lowerIndexpr = NIL;
att->relname = pstrdup(RelationGetRelationName(rd)->data);
att->attrs = lcons(makeString(colname), NIL);
target_expr = (Expr *) handleNestedDots(pstate, att,
&pstate->p_last_resno);
while (ar != NIL)
{
A_Indices *ind = lfirst(ar);
if (lowerIndexpr || (!upperIndexpr && ind->lidx))
{
/*
* XXX assume all lowerIndexpr is non-null in this
* case
*/
lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
}
upperIndexpr = lappend(upperIndexpr, ind->uidx);
ar = lnext(ar);
}
expr = (Node *) make_array_set(target_expr,
upperIndexpr,
lowerIndexpr,
(Expr *) expr);
attrtype = att_typeid(rd, resdomno);
attrlen = tlen(get_id_type(attrtype));
}
}
else
{
resdomno = pstate->p_last_resno++;
attrtype = type_id;
attrlen = type_len;
}
tent = makeNode(TargetEntry);
resnode = makeResdom((AttrNumber) resdomno,
(Oid) attrtype,
(Size) attrlen,
colname,
(Index) 0,
(Oid) 0,
0);
tent->resdom = resnode;
tent->expr = expr;
return tent;
}
/*****************************************************************************
*
* Where Clause
*
*****************************************************************************/
/*
* transformWhereClause -
* transforms the qualification and make sure it is of type Boolean
*
*/
static Node *
transformWhereClause(ParseState *pstate, Node *a_expr)
{
Node *qual;
if (a_expr == NULL)
return (Node *) NULL; /* no qualifiers */
inWhereClause = true;
qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST);
inWhereClause = false;
if (exprType(qual) != BOOLOID)
{
elog(WARN,
"where clause must return type bool, not %s",
tname(get_id_type(exprType(qual))));
}
return qual;
}
/*****************************************************************************
*
* Sort Clause
*
*****************************************************************************/
/*
* find_targetlist_entry -
* returns the Resdom in the target list matching the specified varname
* and range
*
*/
static TargetEntry *
find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist)
{
List *i;
int real_rtable_pos = 0,
target_pos = 0;
TargetEntry *target_result = NULL;
if (sortgroupby->range)
real_rtable_pos = refnameRangeTablePosn(pstate->p_rtable,
sortgroupby->range);
foreach(i, tlist)
{
TargetEntry *target = (TargetEntry *) lfirst(i);
Resdom *resnode = target->resdom;
Var *var = (Var *) target->expr;
char *resname = resnode->resname;
int test_rtable_pos = var->varno;
#ifdef PARSEDEBUG
printf("find_targetlist_entry- target name is %s, position %d, resno %d\n",
(sortgroupby->name ? sortgroupby->name : "(null)"), target_pos + 1, sortgroupby->resno);
#endif
if (!sortgroupby->name)
{
if (sortgroupby->resno == ++target_pos)
{
target_result = target;
break;
}
}
else
{
if (!strcmp(resname, sortgroupby->name))
{
if (sortgroupby->range)
{
if (real_rtable_pos == test_rtable_pos)
{
if (target_result != NULL)
elog(WARN, "Order/Group By %s is ambiguous", sortgroupby->name);
else
target_result = target;
}
}
else
{
if (target_result != NULL)
elog(WARN, "Order/Group By %s is ambiguous", sortgroupby->name);
else
target_result = target;
}
}
}
}
return target_result;
}
static Oid
any_ordering_op(int restype)
{
Operator order_op;
Oid order_opid;
order_op = oper("<", restype, restype, false);
order_opid = oprid(order_op);
return order_opid;
}
/*
* transformGroupClause -
* transform a Group By clause
*
*/
static List *
transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
{
List *glist = NIL,
*gl = NIL;
while (grouplist != NIL)
{
GroupClause *grpcl = makeNode(GroupClause);
TargetEntry *restarget;
Resdom *resdom;
restarget = find_targetlist_entry(pstate, lfirst(grouplist), targetlist);
if (restarget == NULL)
elog(WARN, "The field being grouped by must appear in the target list");
grpcl->entry = restarget;
resdom = restarget->resdom;
grpcl->grpOpoid = oprid(oper("<",
resdom->restype,
resdom->restype, false));
if (glist == NIL)
gl = glist = lcons(grpcl, NIL);
else
{
lnext(gl) = lcons(grpcl, NIL);
gl = lnext(gl);
}
grouplist = lnext(grouplist);
}
return glist;
}
/*
* transformSortClause -
* transform an Order By clause
*
*/
static List *
transformSortClause(ParseState *pstate,
List *orderlist, List *targetlist,
char *uniqueFlag)
{
List *sortlist = NIL;
List *s = NIL,
*i;
while (orderlist != NIL)
{
SortGroupBy *sortby = lfirst(orderlist);
SortClause *sortcl = makeNode(SortClause);
TargetEntry *restarget;
Resdom *resdom;
restarget = find_targetlist_entry(pstate, sortby, targetlist);
if (restarget == NULL)
elog(WARN, "The field being ordered by must appear in the target list");
sortcl->resdom = resdom = restarget->resdom;
sortcl->opoid = oprid(oper(sortby->useOp,
resdom->restype,
resdom->restype, false));
if (sortlist == NIL)
{
s = sortlist = lcons(sortcl, NIL);
}
else
{
lnext(s) = lcons(sortcl, NIL);
s = lnext(s);
}
orderlist = lnext(orderlist);
}
if (uniqueFlag)
{
if (uniqueFlag[0] == '*')
{
/*
* concatenate all elements from target list that are not
* already in the sortby list
*/
foreach(i, targetlist)
{
TargetEntry *tlelt = (TargetEntry *) lfirst(i);
s = sortlist;
while (s != NIL)
{
SortClause *sortcl = lfirst(s);
if (sortcl->resdom == tlelt->resdom)
break;
s = lnext(s);
}
if (s == NIL)
{
/* not a member of the sortclauses yet */
SortClause *sortcl = makeNode(SortClause);
sortcl->resdom = tlelt->resdom;
sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
sortlist = lappend(sortlist, sortcl);
}
}
}
else
{
TargetEntry *tlelt = NULL;
char *uniqueAttrName = uniqueFlag;
/* only create sort clause with the specified unique attribute */
foreach(i, targetlist)
{
tlelt = (TargetEntry *) lfirst(i);
if (strcmp(tlelt->resdom->resname, uniqueAttrName) == 0)
break;
}
if (i == NIL)
{
elog(WARN, "The field specified in the UNIQUE ON clause is not in the targetlist");
}
s = sortlist;
foreach(s, sortlist)
{
SortClause *sortcl = lfirst(s);
if (sortcl->resdom == tlelt->resdom)
break;
}
if (s == NIL)
{
/* not a member of the sortclauses yet */
SortClause *sortcl = makeNode(SortClause);
sortcl->resdom = tlelt->resdom;
sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
sortlist = lappend(sortlist, sortcl);
}
}
}
return sortlist;
}
/*
** HandleNestedDots --
** Given a nested dot expression (i.e. (relation func ... attr), build up
** a tree with of Iter and Func nodes.
*/
static Node *
handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno)
{
List *mutator_iter;
Node *retval = NULL;
if (attr->paramNo != NULL)
{
Param *param = (Param *) transformExpr(pstate, (Node *) attr->paramNo, EXPR_RELATION_FIRST);
retval =
ParseFunc(pstate, strVal(lfirst(attr->attrs)),
lcons(param, NIL),
curr_resno);
}
else
{
Ident *ident = makeNode(Ident);
ident->name = attr->relname;
ident->isRel = TRUE;
retval =
ParseFunc(pstate, strVal(lfirst(attr->attrs)),
lcons(ident, NIL),
curr_resno);
}
foreach(mutator_iter, lnext(attr->attrs))
{
retval = ParseFunc(pstate, strVal(lfirst(mutator_iter)),
lcons(retval, NIL),
curr_resno);
}
return (retval);
}
/*
** make_arguments --
** Given the number and types of arguments to a function, and the
** actual arguments and argument types, do the necessary typecasting.
*/
static void
make_arguments(int nargs,
List *fargs,
Oid *input_typeids,
Oid *function_typeids)
{
/*
* there are two ways an input typeid can differ from a function
* typeid : either the input type inherits the function type, so no
* typecasting is necessary, or the input type can be typecast into
* the function type. right now, we only typecast unknowns, and that
* is all we check for.
*/
List *current_fargs;
int i;
for (i = 0, current_fargs = fargs;
i < nargs;
i++, current_fargs = lnext(current_fargs))
{
if (input_typeids[i] == UNKNOWNOID && function_typeids[i] != InvalidOid)
{
lfirst(current_fargs) =
parser_typecast2(lfirst(current_fargs),
input_typeids[i],
get_id_type(function_typeids[i]),
-1);
}
}
}
/*
** setup_tlist --
** Build a tlist that says which attribute to project to.
** This routine is called by ParseFunc() to set up a target list
** on a tuple parameter or return value. Due to a bug in 4.0,
** it's not possible to refer to system attributes in this case.
*/
static List *
setup_tlist(char *attname, Oid relid)
{
TargetEntry *tle;
Resdom *resnode;
Var *varnode;
Oid typeid;
int attno;
attno = get_attnum(relid, attname);
if (attno < 0)
elog(WARN, "cannot reference attribute %s of tuple params/return values for functions", attname);
typeid = find_atttype(relid, attname);
resnode = makeResdom(1,
typeid,
tlen(get_id_type(typeid)),
get_attname(relid, attno),
0,
(Oid) 0,
0);
varnode = makeVar(-1, attno, typeid, -1, attno);
tle = makeNode(TargetEntry);
tle->resdom = resnode;
tle->expr = (Node *) varnode;
return (lcons(tle, NIL));
}
/*
** setup_base_tlist --
** Build a tlist that extracts a base type from the tuple
** returned by the executor.
*/
static List *
setup_base_tlist(Oid typeid)
{
TargetEntry *tle;
Resdom *resnode;
Var *varnode;
resnode = makeResdom(1,
typeid,
tlen(get_id_type(typeid)),
"<noname>",
0,
(Oid) 0,
0);
varnode = makeVar(-1, 1, typeid, -1, 1);
tle = makeNode(TargetEntry);
tle->resdom = resnode;
tle->expr = (Node *) varnode;
return (lcons(tle, NIL));
}
/*
* ParseComplexProjection -
* handles function calls with a single argument that is of complex type.
* This routine returns NULL if it can't handle the projection (eg. sets).
*/
static Node *
ParseComplexProjection(ParseState *pstate,
char *funcname,
Node *first_arg,
bool *attisset)
{
Oid argtype;
Oid argrelid;
Name relname;
Relation rd;
Oid relid;
int attnum;
switch (nodeTag(first_arg))
{
case T_Iter:
{
Func *func;
Iter *iter;
iter = (Iter *) first_arg;
func = (Func *) ((Expr *) iter->iterexpr)->oper;
argtype = funcid_get_rettype(func->funcid);
argrelid = typeid_get_relid(argtype);
if (argrelid &&
((attnum = get_attnum(argrelid, funcname))
!= InvalidAttrNumber))
{
/*
* the argument is a function returning a tuple, so
* funcname may be a projection
*/
/* add a tlist to the func node and return the Iter */
rd = heap_openr(tname(get_id_type(argtype)));
if (RelationIsValid(rd))
{
relid = RelationGetRelationId(rd);
relname = RelationGetRelationName(rd);
heap_close(rd);
}
if (RelationIsValid(rd))
{
func->func_tlist =
setup_tlist(funcname, argrelid);
iter->itertype = att_typeid(rd, attnum);
return ((Node *) iter);
}
else
{
elog(WARN,
"Function %s has bad returntype %d",
funcname, argtype);
}
}
else
{
/* drop through */
;
}
break;
}
case T_Var:
{
/*
* The argument is a set, so this is either a projection
* or a function call on this set.
*/
*attisset = true;
break;
}
case T_Expr:
{
Expr *expr = (Expr *) first_arg;
Func *funcnode;
if (expr->opType != FUNC_EXPR)
break;
funcnode = (Func *) expr->oper;
argtype = funcid_get_rettype(funcnode->funcid);
argrelid = typeid_get_relid(argtype);
/*
* the argument is a function returning a tuple, so
* funcname may be a projection
*/
if (argrelid &&
(attnum = get_attnum(argrelid, funcname))
!= InvalidAttrNumber)
{
/* add a tlist to the func node */
rd = heap_openr(tname(get_id_type(argtype)));
if (RelationIsValid(rd))
{
relid = RelationGetRelationId(rd);
relname = RelationGetRelationName(rd);
heap_close(rd);
}
if (RelationIsValid(rd))
{
Expr *newexpr;
funcnode->func_tlist =
setup_tlist(funcname, argrelid);
funcnode->functype = att_typeid(rd, attnum);
newexpr = makeNode(Expr);
newexpr->typeOid = funcnode->functype;
newexpr->opType = FUNC_EXPR;
newexpr->oper = (Node *) funcnode;
newexpr->args = lcons(first_arg, NIL);
return ((Node *) newexpr);
}
}
elog(WARN, "Function %s has bad returntype %d",
funcname, argtype);
break;
}
case T_Param:
{
Param *param = (Param *) first_arg;
/*
* If the Param is a complex type, this could be a
* projection
*/
rd = heap_openr(tname(get_id_type(param->paramtype)));
if (RelationIsValid(rd))
{
relid = RelationGetRelationId(rd);
relname = RelationGetRelationName(rd);
heap_close(rd);
}
if (RelationIsValid(rd) &&
(attnum = get_attnum(relid, funcname))
!= InvalidAttrNumber)
{
param->paramtype = att_typeid(rd, attnum);
param->param_tlist = setup_tlist(funcname, relid);
return ((Node *) param);
}
break;
}
default:
break;
}
return NULL;
}
static Node *
ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
{
Oid rettype = (Oid) 0;
Oid argrelid = (Oid) 0;
Oid funcid = (Oid) 0;
List *i = NIL;
Node *first_arg = NULL;
char *relname = NULL;
char *refname = NULL;
Relation rd;
Oid relid;
int nargs;
Func *funcnode;
Oid oid_array[8];
Oid *true_oid_array;
Node *retval;
bool retset;
bool exists;
bool attisset = false;
Oid toid = (Oid) 0;
Expr *expr;
if (fargs)
{
first_arg = lfirst(fargs);
if (first_arg == NULL)
elog(WARN, "function %s does not allow NULL input", funcname);
}
/*
* * check for projection methods: if function takes one argument, and *
* that argument is a relation, param, or PQ function returning a
* complex * type, then the function could be a projection.
*/
if (length(fargs) == 1)
{
if (nodeTag(first_arg) == T_Ident && ((Ident *) first_arg)->isRel)
{
RangeTblEntry *rte;
Ident *ident = (Ident *) first_arg;
/*
* first arg is a relation. This could be a projection.
*/
refname = ident->name;
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
if (rte == NULL)
rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE, NULL);
relname = rte->relname;
relid = rte->relid;
/*
* If the attr isn't a set, just make a var for it. If it is
* a set, treat it like a function and drop through.
*/
if (get_attnum(relid, funcname) != InvalidAttrNumber)
{
Oid dummyTypeId;
return
((Node *) make_var(pstate,
refname,
funcname,
&dummyTypeId));
}
else
{
/* drop through - attr is a set */
;
}
}
else if (ISCOMPLEX(exprType(first_arg)))
{
/*
* Attempt to handle projection of a complex argument. If
* ParseComplexProjection can't handle the projection, we have
* to keep going.
*/
retval = ParseComplexProjection(pstate,
funcname,
first_arg,
&attisset);
if (attisset)
{
toid = exprType(first_arg);
rd = heap_openr(tname(get_id_type(toid)));
if (RelationIsValid(rd))
{
relname = RelationGetRelationName(rd)->data;
heap_close(rd);
}
else
elog(WARN,
"Type %s is not a relation type",
tname(get_id_type(toid)));
argrelid = typeid_get_relid(toid);
/*
* A projection contains either an attribute name or the
* "*".
*/
if ((get_attnum(argrelid, funcname) == InvalidAttrNumber)
&& strcmp(funcname, "*"))
{
elog(WARN, "Functions on sets are not yet supported");
}
}
if (retval)
return retval;
}
else
{
/*
* Parsing aggregates.
*/
Oid basetype;
/*
* the aggregate count is a special case, ignore its base
* type. Treat it as zero
*/
if (strcmp(funcname, "count") == 0)
basetype = 0;
else
basetype = exprType(lfirst(fargs));
if (SearchSysCacheTuple(AGGNAME,
PointerGetDatum(funcname),
ObjectIdGetDatum(basetype),
0, 0))
{
Aggreg *aggreg = ParseAgg(funcname, basetype, lfirst(fargs));
AddAggToParseState(pstate, aggreg);
return (Node *) aggreg;
}
}
}
/*
* * If we dropped through to here it's really a function (or a set,
* which * is implemented as a function.) * extract arg type info and
* transform relation name arguments into * varnodes of the
* appropriate form.
*/
1997-09-18 22:22:58 +02:00
MemSet(&oid_array[0], 0, 8 * sizeof(Oid));
nargs = 0;
foreach(i, fargs)
{
int vnum;
RangeTblEntry *rte;
Node *pair = lfirst(i);
if (nodeTag(pair) == T_Ident && ((Ident *) pair)->isRel)
{
/*
* a relation
*/
refname = ((Ident *) pair)->name;
rte = refnameRangeTableEntry(pstate->p_rtable, refname);
if (rte == NULL)
rte = addRangeTableEntry(pstate, refname, refname,
FALSE, FALSE, NULL);
relname = rte->relname;
vnum = refnameRangeTablePosn(pstate->p_rtable, rte->refname);
/*
* for func(relname), the param to the function is the tuple
* under consideration. we build a special VarNode to reflect
* this -- it has varno set to the correct range table entry,
* but has varattno == 0 to signal that the whole tuple is the
* argument.
*/
toid = typeid(type(relname));
/* replace it in the arg list */
lfirst(fargs) =
makeVar(vnum, 0, toid, vnum, 0);
}
else if (!attisset)
{ /* set functions don't have parameters */
/*
* any functiona args which are typed "unknown", but aren't
* constants, we don't know what to do with, because we can't
* cast them - jolly
*/
if (exprType(pair) == UNKNOWNOID &&
!IsA(pair, Const))
{
elog(WARN, "ParseFunc: no function named %s that takes in an unknown type as argument #%d", funcname, nargs);
}
else
toid = exprType(pair);
}
oid_array[nargs++] = toid;
}
/*
* func_get_detail looks up the function in the catalogs, does
* disambiguation for polymorphic functions, handles inheritance, and
* returns the funcid and type and set or singleton status of the
* function's return value. it also returns the true argument types
* to the function. if func_get_detail returns true, the function
* exists. otherwise, there was an error.
*/
if (attisset)
{ /* we know all of these fields already */
/*
* We create a funcnode with a placeholder function SetEval.
* SetEval() never actually gets executed. When the function
* evaluation routines see it, they use the funcid projected out
* from the relation as the actual function to call. Example:
* retrieve (emp.mgr.name) The plan for this will scan the emp
* relation, projecting out the mgr attribute, which is a funcid.
* This function is then called (instead of SetEval) and "name" is
* projected from its result.
*/
funcid = SetEvalRegProcedure;
rettype = toid;
retset = true;
true_oid_array = oid_array;
exists = true;
}
else
{
exists = func_get_detail(funcname, nargs, oid_array, &funcid,
&rettype, &retset, &true_oid_array);
}
if (!exists)
elog(WARN, "no such attribute or function %s", funcname);
/* got it */
funcnode = makeNode(Func);
funcnode->funcid = funcid;
funcnode->functype = rettype;
funcnode->funcisindex = false;
funcnode->funcsize = 0;
funcnode->func_fcache = NULL;
funcnode->func_tlist = NIL;
funcnode->func_planlist = NIL;
/* perform the necessary typecasting */
make_arguments(nargs, fargs, oid_array, true_oid_array);
/*
* for functions returning base types, we want to project out the
* return value. set up a target list to do that. the executor will
* ignore these for c functions, and do the right thing for postquel
* functions.
*/
if (typeid_get_relid(rettype) == InvalidOid)
funcnode->func_tlist = setup_base_tlist(rettype);
/*
* For sets, we want to make a targetlist to project out this
* attribute of the set tuples.
*/
if (attisset)
{
if (!strcmp(funcname, "*"))
{
funcnode->func_tlist =
expandAll(pstate, relname, refname, curr_resno);
}
else
{
funcnode->func_tlist = setup_tlist(funcname, argrelid);
rettype = find_atttype(argrelid, funcname);
}
}
/*
* Sequence handling.
*/
if (funcid == SeqNextValueRegProcedure ||
funcid == SeqCurrValueRegProcedure)
{
Const *seq;
char *seqrel;
1997-09-18 16:32:15 +02:00
text *seqname;
int32 aclcheck_result = -1;
1997-09-18 16:32:15 +02:00
extern text *lower (text *string);
Assert(length(fargs) == 1);
seq = (Const *) lfirst(fargs);
if (!IsA((Node *) seq, Const))
elog(WARN, "%s: only constant sequence names are acceptable", funcname);
1997-09-18 16:32:15 +02:00
seqname = lower ((text*)DatumGetPointer(seq->constvalue));
pfree (DatumGetPointer(seq->constvalue));
seq->constvalue = PointerGetDatum (seqname);
seqrel = textout(seqname);
if ((aclcheck_result = pg_aclcheck(seqrel, GetPgUserName(),
((funcid == SeqNextValueRegProcedure) ? ACL_WR : ACL_RD)))
!= ACLCHECK_OK)
elog(WARN, "%s.%s: %s",
seqrel, funcname, aclcheck_error_strings[aclcheck_result]);
pfree(seqrel);
if (funcid == SeqNextValueRegProcedure && inWhereClause)
elog(WARN, "nextval of a sequence in WHERE disallowed");
}
expr = makeNode(Expr);
expr->typeOid = rettype;
expr->opType = FUNC_EXPR;
expr->oper = (Node *) funcnode;
expr->args = fargs;
retval = (Node *) expr;
/*
* if the function returns a set of values, then we need to iterate
* over all the returned values in the executor, so we stick an iter
* node here. if it returns a singleton, then we don't need the iter
* node.
*/
if (retset)
{
Iter *iter = makeNode(Iter);
iter->itertype = rettype;
iter->iterexpr = retval;
retval = (Node *) iter;
}
return (retval);
}
/*****************************************************************************
*
*****************************************************************************/
/*
* AddAggToParseState -
* add the aggregate to the list of unique aggregates in pstate.
*
* SIDE EFFECT: aggno in target list entry will be modified
*/
static void
AddAggToParseState(ParseState *pstate, Aggreg *aggreg)
{
List *ag;
int i;
/*
* see if we have the aggregate already (we only need to record the
* aggregate once)
*/
i = 0;
foreach(ag, pstate->p_aggs)
{
Aggreg *a = lfirst(ag);
if (!strcmp(a->aggname, aggreg->aggname) &&
equal(a->target, aggreg->target))
{
/* fill in the aggno and we're done */
aggreg->aggno = i;
return;
}
i++;
}
/* not found, new aggregate */
aggreg->aggno = i;
pstate->p_numAgg++;
pstate->p_aggs = lappend(pstate->p_aggs, aggreg);
return;
}
/*
* finalizeAggregates -
* fill in qry_aggs from pstate. Also checks to make sure that aggregates
* are used in the proper place.
*/
static void
finalizeAggregates(ParseState *pstate, Query *qry)
{
List *l;
int i;
parseCheckAggregates(pstate, qry);
qry->qry_numAgg = pstate->p_numAgg;
qry->qry_aggs =
(Aggreg **) palloc(sizeof(Aggreg *) * qry->qry_numAgg);
i = 0;
foreach(l, pstate->p_aggs)
qry->qry_aggs[i++] = (Aggreg *) lfirst(l);
}
/*
* contain_agg_clause--
* Recursively find aggreg nodes from a clause.
*
* Returns true if any aggregate found.
*/
static bool
contain_agg_clause(Node *clause)
{
if (clause == NULL)
return FALSE;
else if (IsA(clause, Aggreg))
return TRUE;
else if (IsA(clause, Iter))
return contain_agg_clause(((Iter *) clause)->iterexpr);
else if (single_node(clause))
return FALSE;
else if (or_clause(clause))
{
List *temp;
foreach(temp, ((Expr *) clause)->args)
if (contain_agg_clause(lfirst(temp)))
return TRUE;
return FALSE;
}
else if (is_funcclause(clause))
{
List *temp;
foreach(temp, ((Expr *) clause)->args)
if (contain_agg_clause(lfirst(temp)))
return TRUE;
return FALSE;
}
else if (IsA(clause, ArrayRef))
{
List *temp;
foreach(temp, ((ArrayRef *) clause)->refupperindexpr)
if (contain_agg_clause(lfirst(temp)))
return TRUE;
foreach(temp, ((ArrayRef *) clause)->reflowerindexpr)
if (contain_agg_clause(lfirst(temp)))
return TRUE;
if (contain_agg_clause(((ArrayRef *) clause)->refexpr))
return TRUE;
if (contain_agg_clause(((ArrayRef *) clause)->refassgnexpr))
return TRUE;
return FALSE;
}
else if (not_clause(clause))
return contain_agg_clause((Node *) get_notclausearg((Expr *) clause));
else if (is_opclause(clause))
return (contain_agg_clause((Node *) get_leftop((Expr *) clause)) ||
contain_agg_clause((Node *) get_rightop((Expr *) clause)));
return FALSE;
}
/*
* exprIsAggOrGroupCol -
* returns true if the expression does not contain non-group columns.
*/
static bool
exprIsAggOrGroupCol(Node *expr, List *groupClause)
{
List *gl;
if (expr == NULL || IsA(expr, Const) ||
IsA(expr, Param) ||IsA(expr, Aggreg))
return TRUE;
foreach(gl, groupClause)
{
GroupClause *grpcl = lfirst(gl);
if (equal(expr, grpcl->entry->expr))
return TRUE;
}
if (IsA(expr, Expr))
{
List *temp;
foreach(temp, ((Expr *) expr)->args)
if (!exprIsAggOrGroupCol(lfirst(temp), groupClause))
return FALSE;
return TRUE;
}
return FALSE;
}
1997-04-05 08:29:03 +02:00
/*
* tleIsAggOrGroupCol -
* returns true if the TargetEntry is Agg or GroupCol.
1997-04-05 08:29:03 +02:00
*/
static bool
tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause)
1997-04-05 08:29:03 +02:00
{
Node *expr = tle->expr;
List *gl;
if (expr == NULL || IsA(expr, Const) ||IsA(expr, Param))
return TRUE;
foreach(gl, groupClause)
{
GroupClause *grpcl = lfirst(gl);
if (tle->resdom->resno == grpcl->entry->resdom->resno)
{
if (contain_agg_clause((Node *) expr))
elog(WARN, "parser: aggregates not allowed in GROUP BY clause");
return TRUE;
}
1997-04-05 08:29:03 +02:00
}
if (IsA(expr, Aggreg))
return TRUE;
1997-04-05 08:29:03 +02:00
if (IsA(expr, Expr))
{
List *temp;
foreach(temp, ((Expr *) expr)->args)
if (!exprIsAggOrGroupCol(lfirst(temp), groupClause))
return FALSE;
return TRUE;
}
return FALSE;
}
/*
* parseCheckAggregates -
* this should really be done earlier but the current grammar
* cannot differentiate functions from aggregates. So we have do check
* here when the target list and the qualifications are finalized.
*/
static void
parseCheckAggregates(ParseState *pstate, Query *qry)
{
List *tl;
Assert(pstate->p_numAgg > 0);
/*
* aggregates never appear in WHERE clauses. (we have to check where
* clause first because if there is an aggregate, the check for
* non-group column in target list may fail.)
*/
if (contain_agg_clause(qry->qual))
elog(WARN, "parser: aggregates not allowed in WHERE clause");
/*
* the target list can only contain aggregates, group columns and
* functions thereof.
*/
foreach(tl, qry->targetList)
{
TargetEntry *tle = lfirst(tl);
if (!tleIsAggOrGroupCol(tle, qry->groupClause))
elog(WARN,
"parser: illegal use of aggregates or non-group column in target list");
}
/*
* the expression specified in the HAVING clause has the same
* restriction as those in the target list.
*/
1997-04-05 08:29:03 +02:00
/*
* Need to change here when we get HAVING works. Currently
* qry->havingQual is NULL. - vadim 04/05/97
if (!exprIsAggOrGroupCol(qry->havingQual, qry->groupClause))
elog(WARN,
"parser: illegal use of aggregates or non-group column in HAVING clause");
*/
return;
}