summaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>1997-11-25 22:07:18 +0000
committerBruce Momjian <bruce@momjian.us>1997-11-25 22:07:18 +0000
commit4a5b781d71b61887fd312112d75979f250bf723f (patch)
tree315803e512d9e978301311a92866a8b6f17a592d /src/backend/parser
parent3aff4011c735faa747ce94d20da6fd9f85144955 (diff)
downloadpostgresql-4a5b781d71b61887fd312112d75979f250bf723f.tar.gz
Break parser functions into smaller files, group together.
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/Makefile7
-rw-r--r--src/backend/parser/analyze.c2692
-rw-r--r--src/backend/parser/catalog_utils.c1686
-rw-r--r--src/backend/parser/gram.y34
-rw-r--r--src/backend/parser/keywords.c4
-rw-r--r--src/backend/parser/parse_agg.c371
-rw-r--r--src/backend/parser/parse_clause.c407
-rw-r--r--src/backend/parser/parse_expr.c694
-rw-r--r--src/backend/parser/parse_func.c1264
-rw-r--r--src/backend/parser/parse_node.c (renamed from src/backend/parser/parse_query.c)453
-rw-r--r--src/backend/parser/parse_oper.c613
-rw-r--r--src/backend/parser/parse_relation.c480
-rw-r--r--src/backend/parser/parse_target.c679
-rw-r--r--src/backend/parser/parse_type.c319
-rw-r--r--src/backend/parser/parser.c28
-rw-r--r--src/backend/parser/scansup.c3
16 files changed, 4974 insertions, 4760 deletions
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile
index 8559100ee3..7ab45e4bee 100644
--- a/src/backend/parser/Makefile
+++ b/src/backend/parser/Makefile
@@ -4,7 +4,7 @@
# Makefile for parser
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.11 1997/11/24 05:20:57 momjian Exp $
+# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.12 1997/11/25 22:00:21 momjian Exp $
#
#-------------------------------------------------------------------------
@@ -22,8 +22,9 @@ CFLAGS+= -Wno-error
endif
-OBJS= analyze.o catalog_utils.o gram.o \
- keywords.o parser.o parse_query.o scan.o scansup.o
+OBJS= analyze.o gram.o keywords.o parser.o parse_agg.o parse_clause.o \
+ parse_expr.o parse_func.o parse_node.o parse_oper.o parse_relation.o \
+ parse_type.o parse_target.o scan.o scansup.o
all: SUBSYS.o
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index bee5132a8b..4a3800a8a4 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -7,46 +7,30 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.49 1997/11/20 23:22:11 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.50 1997/11/25 22:00:27 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_aggregate.h"
-#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() */
+#include "access/heapam.h"
+#include "nodes/makefuncs.h"
+#include "nodes/memnodes.h"
+#include "nodes/pg_list.h"
+#include "parser/analyze.h"
+#include "parser/parse_agg.h"
+#include "parser/parse_node.h"
+#include "parser/parse_relation.h"
+#include "parser/parse_target.h"
+#include "parser/parse_clause.h"
+#include "utils/builtins.h"
+#include "utils/mcxt.h"
-/* 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);
@@ -55,74 +39,7 @@ 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);
-static Node *parser_typecast(Value *expr, TypeName *typename, int typlen);
-static Node *parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen);
-static Aggreg *ParseAgg(char *aggname, Oid basetype, Node *target);
-
-/*****************************************************************************
- *
- *****************************************************************************/
-
-/*
- * 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);
-}
/*
* parse_analyze -
@@ -144,11 +61,9 @@ parse_analyze(List *pl)
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();
+ pstate = make_parsestate();
result->qtrees[i++] = transformStmt(pstate, lfirst(pl));
pl = lnext(pl);
if (pstate->p_target_relation != NULL)
@@ -620,2580 +535,3 @@ transformCursorStmt(ParseState *pstate, CursorStmt *stmt)
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);
- }
-}
-
-/*
- * 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);
- 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);
- /* 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
- *
- *****************************************************************************/
-
-/*
- * 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);
- 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)
- {
- List *nxt;
- char *name = ((Ident *) lfirst(tl))->name;
-
- /* elog on failure */
- varattno(pstate->p_target_relation, name);
- foreach(nxt, lnext(tl))
- if (!strcmp(name, ((Ident *) lfirst(nxt))->name))
- elog (WARN, "Attribute '%s' should be specified only once", name);
- }
- }
-
- 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);
- }
-
- 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
-
- 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
- {
- List *i;
-
- foreach (i, glist)
- {
- GroupClause *gcl = (GroupClause *) lfirst (i);
-
- if ( gcl->entry == grpcl->entry )
- break;
- }
- if ( i == NIL ) /* not in grouplist already */
- {
- lnext(gl) = lcons(grpcl, NIL);
- gl = lnext(gl);
- }
- else
- pfree (grpcl); /* get rid of this */
- }
- 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;
-
- 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
- {
- List *i;
-
- foreach (i, sortlist)
- {
- SortClause *scl = (SortClause *) lfirst (i);
-
- if ( scl->resdom == sortcl->resdom )
- break;
- }
- if ( i == NIL ) /* not in sortlist already */
- {
- lnext(s) = lcons(sortcl, NIL);
- s = lnext(s);
- }
- else
- pfree (sortcl); /* get rid of this */
- }
- orderlist = lnext(orderlist);
- }
-
- if (uniqueFlag)
- {
- List *i;
-
- 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);
-
- 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.
- */
- 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);
- 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;
- text *seqname;
- int32 aclcheck_result = -1;
- 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);
- 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;
-}
-
-/*
- * tleIsAggOrGroupCol -
- * returns true if the TargetEntry is Agg or GroupCol.
- */
-static bool
-tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause)
-{
- 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;
- }
- }
-
- if (IsA(expr, Aggreg))
- return TRUE;
-
- 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.
- */
-/*
- * 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;
-}
-
-/* not used
-#define PSIZE(PTR) (*((int32 *)(PTR) - 1))
-*/
-
-static Node *
-parser_typecast(Value *expr, TypeName *typename, int typlen)
-{
- /* check for passing non-ints */
- Const *adt;
- Datum lcp;
- Type tp;
- char type_string[NAMEDATALEN];
- int32 len;
- char *cp = NULL;
- char *const_string = NULL;
- bool string_palloced = false;
-
- switch (nodeTag(expr))
- {
- case T_String:
- const_string = DatumGetPointer(expr->val.str);
- break;
- case T_Integer:
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%ld", expr->val.ival);
- break;
- default:
- elog(WARN,
- "parser_typecast: cannot cast this expression to type \"%s\"",
- typename->name);
- }
-
- if (typename->arrayBounds != NIL)
- {
- sprintf(type_string, "_%s", typename->name);
- tp = (Type) type(type_string);
- }
- else
- {
- tp = (Type) type(typename->name);
- }
-
- len = tlen(tp);
-
-#if 0 /* fix me */
- switch (CInteger(lfirst(expr)))
- {
- case INT4OID: /* int4 */
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%d", ((Const *) lnext(expr))->constvalue);
- break;
-
- case NAMEOID: /* char16 */
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%s", ((Const *) lnext(expr))->constvalue);
- break;
-
- case CHAROID: /* char */
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%c", ((Const) lnext(expr))->constvalue);
- break;
-
- case FLOAT8OID: /* float8 */
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%f", ((Const) lnext(expr))->constvalue);
- break;
-
- case CASHOID: /* money */
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%d",
- (int) ((Const *) expr)->constvalue);
- break;
-
- case TEXTOID: /* text */
- const_string = DatumGetPointer(((Const) lnext(expr))->constvalue);
- const_string = (char *) textout((struct varlena *) const_string);
- break;
-
- case UNKNOWNOID: /* unknown */
- const_string = DatumGetPointer(((Const) lnext(expr))->constvalue);
- const_string = (char *) textout((struct varlena *) const_string);
- break;
-
- default:
- elog(WARN, "unknown type %d", CInteger(lfirst(expr)));
- }
-#endif
-
- cp = instr2(tp, const_string, typlen);
-
- if (!tbyvalue(tp))
- {
-/*
- if (len >= 0 && len != PSIZE(cp)) {
- char *pp;
- pp = (char *) palloc(len);
- memmove(pp, cp, len);
- cp = pp;
- }
-*/
- lcp = PointerGetDatum(cp);
- }
- else
- {
- switch (len)
- {
- case 1:
- lcp = Int8GetDatum(cp);
- break;
- case 2:
- lcp = Int16GetDatum(cp);
- break;
- case 4:
- lcp = Int32GetDatum(cp);
- break;
- default:
- lcp = PointerGetDatum(cp);
- break;
- }
- }
-
- adt = makeConst(typeid(tp),
- len,
- (Datum) lcp,
- false,
- tbyvalue(tp),
- false, /* not a set */
- true /* is cast */ );
-
- if (string_palloced)
- pfree(const_string);
-
- return (Node *) adt;
-}
-
-static Node *
-parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen)
-{
- /* check for passing non-ints */
- Const *adt;
- Datum lcp;
- int32 len = tlen(tp);
- char *cp = NULL;
-
- char *const_string = NULL;
- bool string_palloced = false;
-
- Assert(IsA(expr, Const));
-
- switch (exprType)
- {
- case 0: /* NULL */
- break;
- case INT4OID: /* int4 */
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%d",
- (int) ((Const *) expr)->constvalue);
- break;
- case NAMEOID: /* char16 */
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%s",
- (char *) ((Const *) expr)->constvalue);
- break;
- case CHAROID: /* char */
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%c",
- (char) ((Const *) expr)->constvalue);
- break;
- case FLOAT4OID: /* float4 */
- {
- float32 floatVal =
- DatumGetFloat32(((Const *) expr)->constvalue);
-
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%f", *floatVal);
- break;
- }
- case FLOAT8OID: /* float8 */
- {
- float64 floatVal =
- DatumGetFloat64(((Const *) expr)->constvalue);
-
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%f", *floatVal);
- break;
- }
- case CASHOID: /* money */
- const_string = (char *) palloc(256);
- string_palloced = true;
- sprintf(const_string, "%ld",
- (long) ((Const *) expr)->constvalue);
- break;
- case TEXTOID: /* text */
- const_string =
- DatumGetPointer(((Const *) expr)->constvalue);
- const_string = (char *) textout((struct varlena *) const_string);
- break;
- case UNKNOWNOID: /* unknown */
- const_string =
- DatumGetPointer(((Const *) expr)->constvalue);
- const_string = (char *) textout((struct varlena *) const_string);
- break;
- default:
- elog(WARN, "unknown type %u ", exprType);
- }
-
- if (!exprType)
- {
- adt = makeConst(typeid(tp),
- (Size) 0,
- (Datum) NULL,
- true, /* isnull */
- false, /* was omitted */
- false, /* not a set */
- true /* is cast */ );
- return ((Node *) adt);
- }
-
- cp = instr2(tp, const_string, typlen);
-
-
- if (!tbyvalue(tp))
- {
-/*
- if (len >= 0 && len != PSIZE(cp)) {
- char *pp;
- pp = (char *) palloc(len);
- memmove(pp, cp, len);
- cp = pp;
- }
-*/
- lcp = PointerGetDatum(cp);
- }
- else
- {
- switch (len)
- {
- case 1:
- lcp = Int8GetDatum(cp);
- break;
- case 2:
- lcp = Int16GetDatum(cp);
- break;
- case 4:
- lcp = Int32GetDatum(cp);
- break;
- default:
- lcp = PointerGetDatum(cp);
- break;
- }
- }
-
- adt = makeConst(typeid(tp),
- (Size) len,
- (Datum) lcp,
- false,
- false, /* was omitted */
- false, /* not a set */
- true /* is cast */ );
-
- /*
- * printf("adt %s : %u %d %d\n",CString(expr),typeid(tp) , len,cp);
- */
- if (string_palloced)
- pfree(const_string);
-
- return ((Node *) adt);
-}
-
-static Aggreg *
-ParseAgg(char *aggname, Oid basetype, Node *target)
-{
- Oid fintype;
- Oid vartype;
- Oid xfn1;
- Form_pg_aggregate aggform;
- Aggreg *aggreg;
- HeapTuple theAggTuple;
-
- theAggTuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggname),
- ObjectIdGetDatum(basetype),
- 0, 0);
- if (!HeapTupleIsValid(theAggTuple))
- {
- elog(WARN, "aggregate %s does not exist", aggname);
- }
-
- aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
- fintype = aggform->aggfinaltype;
- xfn1 = aggform->aggtransfn1;
-
- if (nodeTag(target) != T_Var && nodeTag(target) != T_Expr)
- elog(WARN, "parser: aggregate can only be applied on an attribute or expression");
-
- /* only aggregates with transfn1 need a base type */
- if (OidIsValid(xfn1))
- {
- basetype = aggform->aggbasetype;
- if (nodeTag(target) == T_Var)
- vartype = ((Var *) target)->vartype;
- else
- vartype = ((Expr *) target)->typeOid;
-
- if (basetype != vartype)
- {
- Type tp1,
- tp2;
-
- tp1 = get_id_type(basetype);
- tp2 = get_id_type(vartype);
- elog(NOTICE, "Aggregate type mismatch:");
- elog(WARN, "%s works on %s, not %s", aggname,
- tname(tp1), tname(tp2));
- }
- }
-
- aggreg = makeNode(Aggreg);
- aggreg->aggname = pstrdup(aggname);
- aggreg->basetype = aggform->aggbasetype;
- aggreg->aggtype = fintype;
-
- aggreg->target = target;
-
- return aggreg;
-}
diff --git a/src/backend/parser/catalog_utils.c b/src/backend/parser/catalog_utils.c
deleted file mode 100644
index b119c7dbb5..0000000000
--- a/src/backend/parser/catalog_utils.c
+++ /dev/null
@@ -1,1686 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * catalog_utils.c--
- *
- * Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/Attic/catalog_utils.c,v 1.30 1997/11/20 23:22:14 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include <string.h>
-#include "postgres.h"
-
-#include "lib/dllist.h"
-#include "utils/datum.h"
-
-#include "utils/builtins.h"
-#include "utils/elog.h"
-#include "utils/palloc.h"
-#include "fmgr.h"
-
-#include "nodes/pg_list.h"
-#include "nodes/parsenodes.h"
-#include "utils/syscache.h"
-#include "catalog/catname.h"
-
-#include "parser/catalog_utils.h"
-#include "catalog/pg_inherits.h"
-#include "catalog/pg_operator.h"
-#include "catalog/pg_type.h"
-#include "catalog/pg_proc.h"
-#include "catalog/indexing.h"
-#include "catalog/catname.h"
-
-#include "access/skey.h"
-#include "access/relscan.h"
-#include "access/tupdesc.h"
-#include "access/htup.h"
-#include "access/heapam.h"
-#include "access/genam.h"
-#include "access/itup.h"
-#include "access/tupmacs.h"
-
-#include "storage/buf.h"
-#include "storage/bufmgr.h"
-#include "utils/lsyscache.h"
-#include "storage/lmgr.h"
-
-#include "port-protos.h" /* strdup() */
-
-struct
-{
- char *field;
- int code;
-} special_attr[] =
-
-{
- {
- "ctid", SelfItemPointerAttributeNumber
- },
- {
- "oid", ObjectIdAttributeNumber
- },
- {
- "xmin", MinTransactionIdAttributeNumber
- },
- {
- "cmin", MinCommandIdAttributeNumber
- },
- {
- "xmax", MaxTransactionIdAttributeNumber
- },
- {
- "cmax", MaxCommandIdAttributeNumber
- },
-};
-
-#define SPECIALS (sizeof(special_attr)/sizeof(*special_attr))
-
-static char *attnum_type[SPECIALS] = {
- "tid",
- "oid",
- "xid",
- "cid",
- "xid",
- "cid",
-};
-
-#define MAXFARGS 8 /* max # args to a c or postquel function */
-
-/*
- * This structure is used to explore the inheritance hierarchy above
- * nodes in the type tree in order to disambiguate among polymorphic
- * functions.
- */
-
-typedef struct _InhPaths
-{
- int nsupers; /* number of superclasses */
- Oid self; /* this class */
- Oid *supervec; /* vector of superclasses */
-} InhPaths;
-
-/*
- * This structure holds a list of possible functions or operators that
- * agree with the known name and argument types of the function/operator.
- */
-typedef struct _CandidateList
-{
- Oid *args;
- struct _CandidateList *next;
-} *CandidateList;
-
-static Oid **argtype_inherit(int nargs, Oid *oid_array);
-static Oid **genxprod(InhPaths *arginh, int nargs);
-static int findsupers(Oid relid, Oid **supervec);
-static bool check_typeid(Oid id);
-static char *instr1(TypeTupleForm tp, char *string, int typlen);
-static void op_error(char *op, Oid arg1, Oid arg2);
-
-/* check to see if a type id is valid,
- * returns true if it is. By using this call before calling
- * get_id_type or get_id_typname, more meaningful error messages
- * can be produced because the caller typically has more context of
- * what's going on - jolly
- */
-static bool
-check_typeid(Oid id)
-{
- return (SearchSysCacheTuple(TYPOID,
- ObjectIdGetDatum(id),
- 0, 0, 0) != NULL);
-}
-
-
-/* return a Type structure, given an typid */
-Type
-get_id_type(Oid id)
-{
- HeapTuple tup;
-
- if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
- 0, 0, 0)))
- {
- elog(WARN, "type id lookup of %ud failed", id);
- return (NULL);
- }
- return ((Type) tup);
-}
-
-/* return a type name, given a typeid */
-char *
-get_id_typname(Oid id)
-{
- HeapTuple tup;
- TypeTupleForm typetuple;
-
- if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
- 0, 0, 0)))
- {
- elog(WARN, "type id lookup of %ud failed", id);
- return (NULL);
- }
- typetuple = (TypeTupleForm) GETSTRUCT(tup);
- return (typetuple->typname).data;
-}
-
-/* return a Type structure, given type name */
-Type
-type(char *s)
-{
- HeapTuple tup;
-
- if (s == NULL)
- {
- elog(WARN, "type(): Null type");
- }
-
- if (!(tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(s), 0, 0, 0)))
- {
- elog(WARN, "type name lookup of %s failed", s);
- }
- return ((Type) tup);
-}
-
-/* given attribute id, return type of that attribute */
-/* XXX Special case for pseudo-attributes is a hack */
-Oid
-att_typeid(Relation rd, int attid)
-{
-
- if (attid < 0)
- {
- return (typeid(type(attnum_type[-attid - 1])));
- }
-
- /*
- * -1 because varattno (where attid comes from) returns one more than
- * index
- */
- return (rd->rd_att->attrs[attid - 1]->atttypid);
-}
-
-
-int
-att_attnelems(Relation rd, int attid)
-{
- return (rd->rd_att->attrs[attid - 1]->attnelems);
-}
-
-/* given type, return the type OID */
-Oid
-typeid(Type tp)
-{
- if (tp == NULL)
- {
- elog(WARN, "typeid() called with NULL type struct");
- }
- return (tp->t_oid);
-}
-
-/* given type (as type struct), return the length of type */
-int16
-tlen(Type t)
-{
- TypeTupleForm typ;
-
- typ = (TypeTupleForm) GETSTRUCT(t);
- return (typ->typlen);
-}
-
-/* given type (as type struct), return the value of its 'byval' attribute.*/
-bool
-tbyval(Type t)
-{
- TypeTupleForm typ;
-
- typ = (TypeTupleForm) GETSTRUCT(t);
- return (typ->typbyval);
-}
-
-/* given type (as type struct), return the name of type */
-char *
-tname(Type t)
-{
- TypeTupleForm typ;
-
- typ = (TypeTupleForm) GETSTRUCT(t);
- return (typ->typname).data;
-}
-
-/* given type (as type struct), return wether type is passed by value */
-int
-tbyvalue(Type t)
-{
- TypeTupleForm typ;
-
- typ = (TypeTupleForm) GETSTRUCT(t);
- return (typ->typbyval);
-}
-
-/* given a type, return its typetype ('c' for 'c'atalog types) */
-static char
-typetypetype(Type t)
-{
- TypeTupleForm typ;
-
- typ = (TypeTupleForm) GETSTRUCT(t);
- return (typ->typtype);
-}
-
-/* given operator, return the operator OID */
-Oid
-oprid(Operator op)
-{
- return (op->t_oid);
-}
-
-/*
- * given opname, leftTypeId and rightTypeId,
- * find all possible (arg1, arg2) pairs for which an operator named
- * opname exists, such that leftTypeId can be coerced to arg1 and
- * rightTypeId can be coerced to arg2
- */
-static int
-binary_oper_get_candidates(char *opname,
- Oid leftTypeId,
- Oid rightTypeId,
- CandidateList *candidates)
-{
- CandidateList current_candidate;
- Relation pg_operator_desc;
- HeapScanDesc pg_operator_scan;
- HeapTuple tup;
- OperatorTupleForm oper;
- Buffer buffer;
- int nkeys;
- int ncandidates = 0;
- ScanKeyData opKey[3];
-
- *candidates = NULL;
-
- ScanKeyEntryInitialize(&opKey[0], 0,
- Anum_pg_operator_oprname,
- NameEqualRegProcedure,
- NameGetDatum(opname));
-
- ScanKeyEntryInitialize(&opKey[1], 0,
- Anum_pg_operator_oprkind,
- CharacterEqualRegProcedure,
- CharGetDatum('b'));
-
-
- if (leftTypeId == UNKNOWNOID)
- {
- if (rightTypeId == UNKNOWNOID)
- {
- nkeys = 2;
- }
- else
- {
- nkeys = 3;
-
- ScanKeyEntryInitialize(&opKey[2], 0,
- Anum_pg_operator_oprright,
- ObjectIdEqualRegProcedure,
- ObjectIdGetDatum(rightTypeId));
- }
- }
- else if (rightTypeId == UNKNOWNOID)
- {
- nkeys = 3;
-
- ScanKeyEntryInitialize(&opKey[2], 0,
- Anum_pg_operator_oprleft,
- ObjectIdEqualRegProcedure,
- ObjectIdGetDatum(leftTypeId));
- }
- else
- {
- /* currently only "unknown" can be coerced */
- return 0;
- }
-
- pg_operator_desc = heap_openr(OperatorRelationName);
- pg_operator_scan = heap_beginscan(pg_operator_desc,
- 0,
- true,
- nkeys,
- opKey);
-
- do
- {
- tup = heap_getnext(pg_operator_scan, 0, &buffer);
- if (HeapTupleIsValid(tup))
- {
- current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
- current_candidate->args = (Oid *) palloc(2 * sizeof(Oid));
-
- oper = (OperatorTupleForm) GETSTRUCT(tup);
- current_candidate->args[0] = oper->oprleft;
- current_candidate->args[1] = oper->oprright;
- current_candidate->next = *candidates;
- *candidates = current_candidate;
- ncandidates++;
- ReleaseBuffer(buffer);
- }
- } while (HeapTupleIsValid(tup));
-
- heap_endscan(pg_operator_scan);
- heap_close(pg_operator_desc);
-
- return ncandidates;
-}
-
-/*
- * equivalentOpersAfterPromotion -
- * checks if a list of candidate operators obtained from
- * binary_oper_get_candidates() contain equivalent operators. If
- * this routine is called, we have more than 1 candidate and need to
- * decided whether to pick one of them. This routine returns true if
- * the all the candidates operate on the same data types after
- * promotion (int2, int4, float4 -> float8).
- */
-static bool
-equivalentOpersAfterPromotion(CandidateList candidates)
-{
- CandidateList result;
- CandidateList promotedCandidates = NULL;
- Oid leftarg,
- rightarg;
-
- for (result = candidates; result != NULL; result = result->next)
- {
- CandidateList c;
-
- c = (CandidateList) palloc(sizeof(*c));
- c->args = (Oid *) palloc(2 * sizeof(Oid));
- switch (result->args[0])
- {
- case FLOAT4OID:
- case INT4OID:
- case INT2OID:
- case CASHOID:
- c->args[0] = FLOAT8OID;
- break;
- default:
- c->args[0] = result->args[0];
- break;
- }
- switch (result->args[1])
- {
- case FLOAT4OID:
- case INT4OID:
- case INT2OID:
- case CASHOID:
- c->args[1] = FLOAT8OID;
- break;
- default:
- c->args[1] = result->args[1];
- break;
- }
- c->next = promotedCandidates;
- promotedCandidates = c;
- }
-
- /*
- * if we get called, we have more than 1 candidates so we can do the
- * following safely
- */
- leftarg = promotedCandidates->args[0];
- rightarg = promotedCandidates->args[1];
-
- for (result = promotedCandidates->next; result != NULL; result = result->next)
- {
- if (result->args[0] != leftarg || result->args[1] != rightarg)
-
- /*
- * this list contains operators that operate on different data
- * types even after promotion. Hence we can't decide on which
- * one to pick. The user must do explicit type casting.
- */
- return FALSE;
- }
-
- /*
- * all the candidates are equivalent in the following sense: they
- * operate on equivalent data types and picking any one of them is as
- * good.
- */
- return TRUE;
-}
-
-
-/*
- * given a choice of argument type pairs for a binary operator,
- * try to choose a default pair
- */
-static CandidateList
-binary_oper_select_candidate(Oid arg1,
- Oid arg2,
- CandidateList candidates)
-{
- CandidateList result;
-
- /*
- * if both are "unknown", there is no way to select a candidate
- *
- * current wisdom holds that the default operator should be one in which
- * both operands have the same type (there will only be one such
- * operator)
- *
- * 7.27.93 - I have decided not to do this; it's too hard to justify, and
- * it's easy enough to typecast explicitly -avi [the rest of this
- * routine were commented out since then -ay]
- */
-
- if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID)
- return (NULL);
-
- /*
- * 6/23/95 - I don't complete agree with avi. In particular, casting
- * floats is a pain for users. Whatever the rationale behind not doing
- * this is, I need the following special case to work.
- *
- * In the WHERE clause of a query, if a float is specified without
- * quotes, we treat it as float8. I added the float48* operators so
- * that we can operate on float4 and float8. But now we have more than
- * one matching operator if the right arg is unknown (eg. float
- * specified with quotes). This break some stuff in the regression
- * test where there are floats in quotes not properly casted. Below is
- * the solution. In addition to requiring the operator operates on the
- * same type for both operands [as in the code Avi originally
- * commented out], we also require that the operators be equivalent in
- * some sense. (see equivalentOpersAfterPromotion for details.) - ay
- * 6/95
- */
- if (!equivalentOpersAfterPromotion(candidates))
- return NULL;
-
- /*
- * if we get here, any one will do but we're more picky and require
- * both operands be the same.
- */
- for (result = candidates; result != NULL; result = result->next)
- {
- if (result->args[0] == result->args[1])
- return result;
- }
-
- return (NULL);
-}
-
-/* Given operator, types of arg1, and arg2, return oper struct */
-/* arg1, arg2 --typeids */
-Operator
-oper(char *op, Oid arg1, Oid arg2, bool noWarnings)
-{
- HeapTuple tup;
- CandidateList candidates;
- int ncandidates;
-
- if (!arg2)
- arg2 = arg1;
- if (!arg1)
- arg1 = arg2;
-
- if (!(tup = SearchSysCacheTuple(OPRNAME,
- PointerGetDatum(op),
- ObjectIdGetDatum(arg1),
- ObjectIdGetDatum(arg2),
- Int8GetDatum('b'))))
- {
- ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);
- if (ncandidates == 0)
- {
-
- /*
- * no operators of the desired types found
- */
- if (!noWarnings)
- op_error(op, arg1, arg2);
- return (NULL);
- }
- else if (ncandidates == 1)
- {
-
- /*
- * exactly one operator of the desired types found
- */
- tup = SearchSysCacheTuple(OPRNAME,
- PointerGetDatum(op),
- ObjectIdGetDatum(candidates->args[0]),
- ObjectIdGetDatum(candidates->args[1]),
- Int8GetDatum('b'));
- Assert(HeapTupleIsValid(tup));
- }
- else
- {
-
- /*
- * multiple operators of the desired types found
- */
- candidates = binary_oper_select_candidate(arg1, arg2, candidates);
- if (candidates != NULL)
- {
- /* we chose one of them */
- tup = SearchSysCacheTuple(OPRNAME,
- PointerGetDatum(op),
- ObjectIdGetDatum(candidates->args[0]),
- ObjectIdGetDatum(candidates->args[1]),
- Int8GetDatum('b'));
- Assert(HeapTupleIsValid(tup));
- }
- else
- {
- Type tp1,
- tp2;
-
- /* we chose none of them */
- tp1 = get_id_type(arg1);
- tp2 = get_id_type(arg2);
- if (!noWarnings)
- {
- elog(NOTICE, "there is more than one operator %s for types", op);
- elog(NOTICE, "%s and %s. You will have to retype this query",
- tname(tp1), tname(tp2));
- elog(WARN, "using an explicit cast");
- }
- return (NULL);
- }
- }
- }
- return ((Operator) tup);
-}
-
-/*
- * given opname and typeId, find all possible types for which
- * a right/left unary operator named opname exists,
- * such that typeId can be coerced to it
- */
-static int
-unary_oper_get_candidates(char *op,
- Oid typeId,
- CandidateList *candidates,
- char rightleft)
-{
- CandidateList current_candidate;
- Relation pg_operator_desc;
- HeapScanDesc pg_operator_scan;
- HeapTuple tup;
- OperatorTupleForm oper;
- Buffer buffer;
- int ncandidates = 0;
-
- static ScanKeyData opKey[2] = {
- {0, Anum_pg_operator_oprname, NameEqualRegProcedure},
- {0, Anum_pg_operator_oprkind, CharacterEqualRegProcedure}};
-
- *candidates = NULL;
-
- fmgr_info(NameEqualRegProcedure, (func_ptr *) &opKey[0].sk_func,
- &opKey[0].sk_nargs);
- opKey[0].sk_argument = NameGetDatum(op);
- fmgr_info(CharacterEqualRegProcedure, (func_ptr *) &opKey[1].sk_func,
- &opKey[1].sk_nargs);
- opKey[1].sk_argument = CharGetDatum(rightleft);
-
- /* currently, only "unknown" can be coerced */
-
- /*
- * but we should allow types that are internally the same to be
- * "coerced"
- */
- if (typeId != UNKNOWNOID)
- {
- return 0;
- }
-
- pg_operator_desc = heap_openr(OperatorRelationName);
- pg_operator_scan = heap_beginscan(pg_operator_desc,
- 0,
- true,
- 2,
- opKey);
-
- do
- {
- tup = heap_getnext(pg_operator_scan, 0, &buffer);
- if (HeapTupleIsValid(tup))
- {
- current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
- current_candidate->args = (Oid *) palloc(sizeof(Oid));
-
- oper = (OperatorTupleForm) GETSTRUCT(tup);
- if (rightleft == 'r')
- current_candidate->args[0] = oper->oprleft;
- else
- current_candidate->args[0] = oper->oprright;
- current_candidate->next = *candidates;
- *candidates = current_candidate;
- ncandidates++;
- ReleaseBuffer(buffer);
- }
- } while (HeapTupleIsValid(tup));
-
- heap_endscan(pg_operator_scan);
- heap_close(pg_operator_desc);
-
- return ncandidates;
-}
-
-/* Given unary right-side operator (operator on right), return oper struct */
-/* arg-- type id */
-Operator
-right_oper(char *op, Oid arg)
-{
- HeapTuple tup;
- CandidateList candidates;
- int ncandidates;
-
- /*
- * if (!OpCache) { init_op_cache(); }
- */
- if (!(tup = SearchSysCacheTuple(OPRNAME,
- PointerGetDatum(op),
- ObjectIdGetDatum(arg),
- ObjectIdGetDatum(InvalidOid),
- Int8GetDatum('r'))))
- {
- ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r');
- if (ncandidates == 0)
- {
- elog(WARN,
- "Can't find right op: %s for type %d", op, arg);
- return (NULL);
- }
- else if (ncandidates == 1)
- {
- tup = SearchSysCacheTuple(OPRNAME,
- PointerGetDatum(op),
- ObjectIdGetDatum(candidates->args[0]),
- ObjectIdGetDatum(InvalidOid),
- Int8GetDatum('r'));
- Assert(HeapTupleIsValid(tup));
- }
- else
- {
- elog(NOTICE, "there is more than one right operator %s", op);
- elog(NOTICE, "you will have to retype this query");
- elog(WARN, "using an explicit cast");
- return (NULL);
- }
- }
- return ((Operator) tup);
-}
-
-/* Given unary left-side operator (operator on left), return oper struct */
-/* arg--type id */
-Operator
-left_oper(char *op, Oid arg)
-{
- HeapTuple tup;
- CandidateList candidates;
- int ncandidates;
-
- /*
- * if (!OpCache) { init_op_cache(); }
- */
- if (!(tup = SearchSysCacheTuple(OPRNAME,
- PointerGetDatum(op),
- ObjectIdGetDatum(InvalidOid),
- ObjectIdGetDatum(arg),
- Int8GetDatum('l'))))
- {
- ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l');
- if (ncandidates == 0)
- {
- elog(WARN,
- "Can't find left op: %s for type %d", op, arg);
- return (NULL);
- }
- else if (ncandidates == 1)
- {
- tup = SearchSysCacheTuple(OPRNAME,
- PointerGetDatum(op),
- ObjectIdGetDatum(InvalidOid),
- ObjectIdGetDatum(candidates->args[0]),
- Int8GetDatum('l'));
- Assert(HeapTupleIsValid(tup));
- }
- else
- {
- elog(NOTICE, "there is more than one left operator %s", op);
- elog(NOTICE, "you will have to retype this query");
- elog(WARN, "using an explicit cast");
- return (NULL);
- }
- }
- return ((Operator) tup);
-}
-
-/* given range variable, return id of variable */
-
-int
-varattno(Relation rd, char *a)
-{
- int i;
-
- for (i = 0; i < rd->rd_rel->relnatts; i++)
- {
- if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a))
- {
- return (i + 1);
- }
- }
- for (i = 0; i < SPECIALS; i++)
- {
- if (!strcmp(special_attr[i].field, a))
- {
- return (special_attr[i].code);
- }
- }
-
- elog(WARN, "Relation %s does not have attribute %s",
- RelationGetRelationName(rd), a);
- return (-1);
-}
-
-/* Given range variable, return whether attribute of this name
- * is a set.
- * NOTE the ASSUMPTION here that no system attributes are, or ever
- * will be, sets.
- */
-bool
-varisset(Relation rd, char *name)
-{
- int i;
-
- /* First check if this is a system attribute */
- for (i = 0; i < SPECIALS; i++)
- {
- if (!strcmp(special_attr[i].field, name))
- {
- return (false); /* no sys attr is a set */
- }
- }
- return (get_attisset(rd->rd_id, name));
-}
-
-/* given range variable, return id of variable */
-int
-nf_varattno(Relation rd, char *a)
-{
- int i;
-
- for (i = 0; i < rd->rd_rel->relnatts; i++)
- {
- if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a))
- {
- return (i + 1);
- }
- }
- for (i = 0; i < SPECIALS; i++)
- {
- if (!strcmp(special_attr[i].field, a))
- {
- return (special_attr[i].code);
- }
- }
- return InvalidAttrNumber;
-}
-
-/*-------------
- * given an attribute number and a relation, return its relation name
- */
-char *
-getAttrName(Relation rd, int attrno)
-{
- char *name;
- int i;
-
- if (attrno < 0)
- {
- for (i = 0; i < SPECIALS; i++)
- {
- if (special_attr[i].code == attrno)
- {
- name = special_attr[i].field;
- return (name);
- }
- }
- elog(WARN, "Illegal attr no %d for relation %s",
- attrno, RelationGetRelationName(rd));
- }
- else if (attrno >= 1 && attrno <= RelationGetNumberOfAttributes(rd))
- {
- name = (rd->rd_att->attrs[attrno - 1]->attname).data;
- return (name);
- }
- else
- {
- elog(WARN, "Illegal attr no %d for relation %s",
- attrno, RelationGetRelationName(rd));
- }
-
- /*
- * Shouldn't get here, but we want lint to be happy...
- */
-
- return (NULL);
-}
-
-/* Given a typename and value, returns the ascii form of the value */
-
-#ifdef NOT_USED
-char *
-outstr(char *typename, /* Name of type of value */
- char *value) /* Could be of any type */
-{
- TypeTupleForm tp;
- Oid op;
-
- tp = (TypeTupleForm) GETSTRUCT(type(typename));
- op = tp->typoutput;
- return ((char *) fmgr(op, value));
-}
-
-#endif
-
-/* Given a Type and a string, return the internal form of that string */
-char *
-instr2(Type tp, char *string, int typlen)
-{
- return (instr1((TypeTupleForm) GETSTRUCT(tp), string, typlen));
-}
-
-/* Given a type structure and a string, returns the internal form of
- that string */
-static char *
-instr1(TypeTupleForm tp, char *string, int typlen)
-{
- Oid op;
- Oid typelem;
-
- op = tp->typinput;
- typelem = tp->typelem; /* XXX - used for array_in */
- /* typlen is for bpcharin() and varcharin() */
- return ((char *) fmgr(op, string, typelem, typlen));
-}
-
-/* Given the attribute type of an array return the arrtribute type of
- an element of the array */
-
-Oid
-GetArrayElementType(Oid typearray)
-{
- HeapTuple type_tuple;
- TypeTupleForm type_struct_array;
-
- type_tuple = SearchSysCacheTuple(TYPOID,
- ObjectIdGetDatum(typearray),
- 0, 0, 0);
-
- if (!HeapTupleIsValid(type_tuple))
- elog(WARN, "GetArrayElementType: Cache lookup failed for type %d",
- 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, "GetArrayElementType: type %s is not an array",
- (Name) &(type_struct_array->typname.data[0]));
- }
-
- return (type_struct_array->typelem);
-}
-
-Oid
-funcid_get_rettype(Oid funcid)
-{
- HeapTuple func_tuple = NULL;
- Oid funcrettype = (Oid) 0;
-
- func_tuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid),
- 0, 0, 0);
-
- if (!HeapTupleIsValid(func_tuple))
- elog(WARN, "function %d does not exist", funcid);
-
- funcrettype = (Oid)
- ((Form_pg_proc) GETSTRUCT(func_tuple))->prorettype;
-
- return (funcrettype);
-}
-
-/*
- * get a list of all argument type vectors for which a function named
- * funcname taking nargs arguments exists
- */
-static CandidateList
-func_get_candidates(char *funcname, int nargs)
-{
- Relation heapRelation;
- Relation idesc;
- ScanKeyData skey;
- HeapTuple tuple;
- IndexScanDesc sd;
- RetrieveIndexResult indexRes;
- Buffer buffer;
- Form_pg_proc pgProcP;
- bool bufferUsed = FALSE;
- CandidateList candidates = NULL;
- CandidateList current_candidate;
- int i;
-
- heapRelation = heap_openr(ProcedureRelationName);
- ScanKeyEntryInitialize(&skey,
- (bits16) 0x0,
- (AttrNumber) 1,
- (RegProcedure) NameEqualRegProcedure,
- (Datum) funcname);
-
- idesc = index_openr(ProcedureNameIndex);
-
- sd = index_beginscan(idesc, false, 1, &skey);
-
- do
- {
- tuple = (HeapTuple) NULL;
- if (bufferUsed)
- {
- ReleaseBuffer(buffer);
- bufferUsed = FALSE;
- }
-
- indexRes = index_getnext(sd, ForwardScanDirection);
- if (indexRes)
- {
- ItemPointer iptr;
-
- iptr = &indexRes->heap_iptr;
- tuple = heap_fetch(heapRelation, false, iptr, &buffer);
- pfree(indexRes);
- if (HeapTupleIsValid(tuple))
- {
- pgProcP = (Form_pg_proc) GETSTRUCT(tuple);
- bufferUsed = TRUE;
- if (pgProcP->pronargs == nargs)
- {
- current_candidate = (CandidateList)
- palloc(sizeof(struct _CandidateList));
- current_candidate->args = (Oid *)
- palloc(8 * sizeof(Oid));
- MemSet(current_candidate->args, 0, 8 * sizeof(Oid));
- for (i = 0; i < nargs; i++)
- {
- current_candidate->args[i] =
- pgProcP->proargtypes[i];
- }
-
- current_candidate->next = candidates;
- candidates = current_candidate;
- }
- }
- }
- } while (indexRes);
-
- index_endscan(sd);
- index_close(idesc);
- heap_close(heapRelation);
-
- return candidates;
-}
-
-/*
- * can input_typeids be coerced to func_typeids?
- */
-static bool
-can_coerce(int nargs, Oid *input_typeids, Oid *func_typeids)
-{
- int i;
- Type tp;
-
- /*
- * right now, we only coerce "unknown", and we cannot coerce it to a
- * relation type
- */
- for (i = 0; i < nargs; i++)
- {
- if (input_typeids[i] != func_typeids[i])
- {
- if ((input_typeids[i] == BPCHAROID && func_typeids[i] == TEXTOID) ||
- (input_typeids[i] == BPCHAROID && func_typeids[i] == VARCHAROID) ||
- (input_typeids[i] == VARCHAROID && func_typeids[i] == TEXTOID) ||
- (input_typeids[i] == VARCHAROID && func_typeids[i] == BPCHAROID) ||
- (input_typeids[i] == CASHOID && func_typeids[i] == INT4OID) ||
- (input_typeids[i] == INT4OID && func_typeids[i] == CASHOID))
- ; /* these are OK */
- else if (input_typeids[i] != UNKNOWNOID || func_typeids[i] == 0)
- return false;
-
- tp = get_id_type(input_typeids[i]);
- if (typetypetype(tp) == 'c')
- return false;
- }
- }
-
- return true;
-}
-
-/*
- * given a list of possible typeid arrays to a function and an array of
- * input typeids, produce a shortlist of those function typeid arrays
- * that match the input typeids (either exactly or by coercion), and
- * return the number of such arrays
- */
-static int
-match_argtypes(int nargs,
- Oid *input_typeids,
- CandidateList function_typeids,
- CandidateList *candidates) /* return value */
-{
- CandidateList current_candidate;
- CandidateList matching_candidate;
- Oid *current_typeids;
- int ncandidates = 0;
-
- *candidates = NULL;
-
- for (current_candidate = function_typeids;
- current_candidate != NULL;
- current_candidate = current_candidate->next)
- {
- current_typeids = current_candidate->args;
- if (can_coerce(nargs, input_typeids, current_typeids))
- {
- matching_candidate = (CandidateList)
- palloc(sizeof(struct _CandidateList));
- matching_candidate->args = current_typeids;
- matching_candidate->next = *candidates;
- *candidates = matching_candidate;
- ncandidates++;
- }
- }
-
- return ncandidates;
-}
-
-/*
- * given the input argtype array and more than one candidate
- * for the function argtype array, attempt to resolve the conflict.
- * returns the selected argtype array if the conflict can be resolved,
- * otherwise returns NULL
- */
-static Oid *
-func_select_candidate(int nargs,
- Oid *input_typeids,
- CandidateList candidates)
-{
- /* XXX no conflict resolution implemeneted yet */
- return (NULL);
-}
-
-bool
-func_get_detail(char *funcname,
- int nargs,
- Oid *oid_array,
- Oid *funcid, /* return value */
- Oid *rettype, /* return value */
- bool *retset, /* return value */
- Oid **true_typeids) /* return value */
-{
- Oid **input_typeid_vector;
- Oid *current_input_typeids;
- CandidateList function_typeids;
- CandidateList current_function_typeids;
- HeapTuple ftup;
- Form_pg_proc pform;
-
- /*
- * attempt to find named function in the system catalogs with
- * arguments exactly as specified - so that the normal case is just as
- * quick as before
- */
- ftup = SearchSysCacheTuple(PRONAME,
- PointerGetDatum(funcname),
- Int32GetDatum(nargs),
- PointerGetDatum(oid_array),
- 0);
- *true_typeids = oid_array;
-
- /*
- * If an exact match isn't found : 1) get a vector of all possible
- * input arg type arrays constructed from the superclasses of the
- * original input arg types 2) get a list of all possible argument
- * type arrays to the function with given name and number of arguments
- * 3) for each input arg type array from vector #1 : a) find how many
- * of the function arg type arrays from list #2 it can be coerced to
- * b) - if the answer is one, we have our function - if the answer is
- * more than one, attempt to resolve the conflict - if the answer is
- * zero, try the next array from vector #1
- */
- if (!HeapTupleIsValid(ftup))
- {
- function_typeids = func_get_candidates(funcname, nargs);
-
- if (function_typeids != NULL)
- {
- int ncandidates = 0;
-
- input_typeid_vector = argtype_inherit(nargs, oid_array);
- current_input_typeids = oid_array;
-
- do
- {
- ncandidates = match_argtypes(nargs, current_input_typeids,
- function_typeids,
- &current_function_typeids);
- if (ncandidates == 1)
- {
- *true_typeids = current_function_typeids->args;
- ftup = SearchSysCacheTuple(PRONAME,
- PointerGetDatum(funcname),
- Int32GetDatum(nargs),
- PointerGetDatum(*true_typeids),
- 0);
- Assert(HeapTupleIsValid(ftup));
- }
- else if (ncandidates > 1)
- {
- *true_typeids =
- func_select_candidate(nargs,
- current_input_typeids,
- current_function_typeids);
- if (*true_typeids == NULL)
- {
- elog(NOTICE, "there is more than one function named \"%s\"",
- funcname);
- elog(NOTICE, "that satisfies the given argument types. you will have to");
- elog(NOTICE, "retype your query using explicit typecasts.");
- func_error("func_get_detail", funcname, nargs, oid_array);
- }
- else
- {
- ftup = SearchSysCacheTuple(PRONAME,
- PointerGetDatum(funcname),
- Int32GetDatum(nargs),
- PointerGetDatum(*true_typeids),
- 0);
- Assert(HeapTupleIsValid(ftup));
- }
- }
- current_input_typeids = *input_typeid_vector++;
- }
- while (current_input_typeids !=
- InvalidOid && ncandidates == 0);
- }
- }
-
- if (!HeapTupleIsValid(ftup))
- {
- Type tp;
-
- if (nargs == 1)
- {
- tp = get_id_type(oid_array[0]);
- if (typetypetype(tp) == 'c')
- elog(WARN, "no such attribute or function \"%s\"",
- funcname);
- }
- func_error("func_get_detail", funcname, nargs, oid_array);
- }
- else
- {
- pform = (Form_pg_proc) GETSTRUCT(ftup);
- *funcid = ftup->t_oid;
- *rettype = pform->prorettype;
- *retset = pform->proretset;
-
- return (true);
- }
-/* shouldn't reach here */
- return (false);
-
-}
-
-/*
- * argtype_inherit() -- Construct an argtype vector reflecting the
- * inheritance properties of the supplied argv.
- *
- * This function is used to disambiguate among functions with the
- * same name but different signatures. It takes an array of eight
- * type ids. For each type id in the array that's a complex type
- * (a class), it walks up the inheritance tree, finding all
- * superclasses of that type. A vector of new Oid type arrays
- * is returned to the caller, reflecting the structure of the
- * inheritance tree above the supplied arguments.
- *
- * The order of this vector is as follows: all superclasses of the
- * rightmost complex class are explored first. The exploration
- * continues from right to left. This policy means that we favor
- * keeping the leftmost argument type as low in the inheritance tree
- * as possible. This is intentional; it is exactly what we need to
- * do for method dispatch. The last type array we return is all
- * zeroes. This will match any functions for which return types are
- * not defined. There are lots of these (mostly builtins) in the
- * catalogs.
- */
-static Oid **
-argtype_inherit(int nargs, Oid *oid_array)
-{
- Oid relid;
- int i;
- InhPaths arginh[MAXFARGS];
-
- for (i = 0; i < MAXFARGS; i++)
- {
- if (i < nargs)
- {
- arginh[i].self = oid_array[i];
- if ((relid = typeid_get_relid(oid_array[i])) != InvalidOid)
- {
- arginh[i].nsupers = findsupers(relid, &(arginh[i].supervec));
- }
- else
- {
- arginh[i].nsupers = 0;
- arginh[i].supervec = (Oid *) NULL;
- }
- }
- else
- {
- arginh[i].self = InvalidOid;
- arginh[i].nsupers = 0;
- arginh[i].supervec = (Oid *) NULL;
- }
- }
-
- /* return an ordered cross-product of the classes involved */
- return (genxprod(arginh, nargs));
-}
-
-typedef struct _SuperQE
-{
- Oid sqe_relid;
-} SuperQE;
-
-static int
-findsupers(Oid relid, Oid **supervec)
-{
- Oid *relidvec;
- Relation inhrel;
- HeapScanDesc inhscan;
- ScanKeyData skey;
- HeapTuple inhtup;
- TupleDesc inhtupdesc;
- int nvisited;
- SuperQE *qentry,
- *vnode;
- Dllist *visited,
- *queue;
- Dlelem *qe,
- *elt;
-
- Relation rd;
- Buffer buf;
- Datum d;
- bool newrelid;
- char isNull;
-
- nvisited = 0;
- queue = DLNewList();
- visited = DLNewList();
-
-
- inhrel = heap_openr(InheritsRelationName);
- RelationSetLockForRead(inhrel);
- inhtupdesc = RelationGetTupleDescriptor(inhrel);
-
- /*
- * Use queue to do a breadth-first traversal of the inheritance graph
- * from the relid supplied up to the root.
- */
- do
- {
- ScanKeyEntryInitialize(&skey, 0x0, Anum_pg_inherits_inhrel,
- ObjectIdEqualRegProcedure,
- ObjectIdGetDatum(relid));
-
- inhscan = heap_beginscan(inhrel, 0, false, 1, &skey);
-
- while (HeapTupleIsValid(inhtup = heap_getnext(inhscan, 0, &buf)))
- {
- qentry = (SuperQE *) palloc(sizeof(SuperQE));
-
- d = fastgetattr(inhtup, Anum_pg_inherits_inhparent,
- inhtupdesc, &isNull);
- qentry->sqe_relid = DatumGetObjectId(d);
-
- /* put this one on the queue */
- DLAddTail(queue, DLNewElem(qentry));
-
- ReleaseBuffer(buf);
- }
-
- heap_endscan(inhscan);
-
- /* pull next unvisited relid off the queue */
- do
- {
- qe = DLRemHead(queue);
- qentry = qe ? (SuperQE *) DLE_VAL(qe) : NULL;
-
- if (qentry == (SuperQE *) NULL)
- break;
-
- relid = qentry->sqe_relid;
- newrelid = true;
-
- for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt))
- {
- vnode = (SuperQE *) DLE_VAL(elt);
- if (vnode && (qentry->sqe_relid == vnode->sqe_relid))
- {
- newrelid = false;
- break;
- }
- }
- } while (!newrelid);
-
- if (qentry != (SuperQE *) NULL)
- {
-
- /* save the type id, rather than the relation id */
- if ((rd = heap_open(qentry->sqe_relid)) == (Relation) NULL)
- elog(WARN, "relid %d does not exist", qentry->sqe_relid);
- qentry->sqe_relid = typeid(type(RelationGetRelationName(rd)->data));
- heap_close(rd);
-
- DLAddTail(visited, qe);
-
- nvisited++;
- }
- } while (qentry != (SuperQE *) NULL);
-
- RelationUnsetLockForRead(inhrel);
- heap_close(inhrel);
-
- if (nvisited > 0)
- {
- relidvec = (Oid *) palloc(nvisited * sizeof(Oid));
- *supervec = relidvec;
-
- for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt))
- {
- vnode = (SuperQE *) DLE_VAL(elt);
- *relidvec++ = vnode->sqe_relid;
- }
-
- }
- else
- {
- *supervec = (Oid *) NULL;
- }
-
- return (nvisited);
-}
-
-static Oid **
-genxprod(InhPaths *arginh, int nargs)
-{
- int nanswers;
- Oid **result,
- **iter;
- Oid *oneres;
- int i,
- j;
- int cur[MAXFARGS];
-
- nanswers = 1;
- for (i = 0; i < nargs; i++)
- {
- nanswers *= (arginh[i].nsupers + 2);
- cur[i] = 0;
- }
-
- iter = result = (Oid **) palloc(sizeof(Oid *) * nanswers);
-
- /* compute the cross product from right to left */
- for (;;)
- {
- oneres = (Oid *) palloc(MAXFARGS * sizeof(Oid));
- MemSet(oneres, 0, MAXFARGS * sizeof(Oid));
-
- for (i = nargs - 1; i >= 0 && cur[i] > arginh[i].nsupers; i--)
- continue;
-
- /* if we're done, terminate with NULL pointer */
- if (i < 0)
- {
- *iter = NULL;
- return (result);
- }
-
- /* no, increment this column and zero the ones after it */
- cur[i] = cur[i] + 1;
- for (j = nargs - 1; j > i; j--)
- cur[j] = 0;
-
- for (i = 0; i < nargs; i++)
- {
- if (cur[i] == 0)
- oneres[i] = arginh[i].self;
- else if (cur[i] > arginh[i].nsupers)
- oneres[i] = 0; /* wild card */
- else
- oneres[i] = arginh[i].supervec[cur[i] - 1];
- }
-
- *iter++ = oneres;
- }
-}
-
-/* Given a type id, returns the in-conversion function of the type */
-Oid
-typeid_get_retinfunc(Oid type_id)
-{
- HeapTuple typeTuple;
- TypeTupleForm type;
- Oid infunc;
-
- typeTuple = SearchSysCacheTuple(TYPOID,
- ObjectIdGetDatum(type_id),
- 0, 0, 0);
- if (!HeapTupleIsValid(typeTuple))
- elog(WARN, "typeid_get_retinfunc: Invalid type - oid = %u", type_id);
-
- type = (TypeTupleForm) GETSTRUCT(typeTuple);
- infunc = type->typinput;
- return (infunc);
-}
-
-/* Given a type id, returns the out-conversion function of the type */
-Oid
-typeid_get_retoutfunc(Oid type_id)
-{
- HeapTuple typeTuple;
- TypeTupleForm type;
- Oid outfunc;
-
- typeTuple = SearchSysCacheTuple(TYPOID,
- ObjectIdGetDatum(type_id),
- 0, 0, 0);
- if (!HeapTupleIsValid(typeTuple))
- elog(WARN, "typeid_get_retoutfunc: Invalid type - oid = %u", type_id);
-
- type = (TypeTupleForm) GETSTRUCT(typeTuple);
- outfunc = type->typoutput;
- return (outfunc);
-}
-
-Oid
-typeid_get_relid(Oid type_id)
-{
- HeapTuple typeTuple;
- TypeTupleForm type;
- Oid infunc;
-
- typeTuple = SearchSysCacheTuple(TYPOID,
- ObjectIdGetDatum(type_id),
- 0, 0, 0);
- if (!HeapTupleIsValid(typeTuple))
- elog(WARN, "typeid_get_relid: Invalid type - oid = %u", type_id);
-
- type = (TypeTupleForm) GETSTRUCT(typeTuple);
- infunc = type->typrelid;
- return (infunc);
-}
-
-Oid
-get_typrelid(Type typ)
-{
- TypeTupleForm typtup;
-
- typtup = (TypeTupleForm) GETSTRUCT(typ);
-
- return (typtup->typrelid);
-}
-
-Oid
-get_typelem(Oid type_id)
-{
- HeapTuple typeTuple;
- TypeTupleForm type;
-
- if (!(typeTuple = SearchSysCacheTuple(TYPOID,
- ObjectIdGetDatum(type_id),
- 0, 0, 0)))
- {
- elog(WARN, "type id lookup of %u failed", type_id);
- }
- type = (TypeTupleForm) GETSTRUCT(typeTuple);
-
- return (type->typelem);
-}
-
-#ifdef NOT_USED
-char
-FindDelimiter(char *typename)
-{
- char delim;
- HeapTuple typeTuple;
- TypeTupleForm type;
-
-
- if (!(typeTuple = SearchSysCacheTuple(TYPNAME,
- PointerGetDatum(typename),
- 0, 0, 0)))
- {
- elog(WARN, "type name lookup of %s failed", typename);
- }
- type = (TypeTupleForm) GETSTRUCT(typeTuple);
-
- delim = type->typdelim;
- return (delim);
-}
-
-#endif
-
-/*
- * Give a somewhat useful error message when the operator for two types
- * is not found.
- */
-static void
-op_error(char *op, Oid arg1, Oid arg2)
-{
- Type tp1 = NULL,
- tp2 = NULL;
-
- if (check_typeid(arg1))
- {
- tp1 = get_id_type(arg1);
- }
- else
- {
- elog(WARN, "left hand side of operator %s has an unknown type, probably a bad attribute name", op);
- }
-
- if (check_typeid(arg2))
- {
- tp2 = get_id_type(arg2);
- }
- else
- {
- elog(WARN, "right hand side of operator %s has an unknown type, probably a bad attribute name", op);
- }
-
- elog(NOTICE, "there is no operator %s for types %s and %s",
- op, tname(tp1), tname(tp2));
- elog(NOTICE, "You will either have to retype this query using an");
- elog(NOTICE, "explicit cast, or you will have to define the operator");
- elog(WARN, "%s for %s and %s using CREATE OPERATOR",
- op, tname(tp1), tname(tp2));
-}
-
-/*
- * Error message when function lookup fails that gives details of the
- * argument types
- */
-void
-func_error(char *caller, char *funcname, int nargs, Oid *argtypes)
-{
- char p[(NAMEDATALEN + 2) * MAXFMGRARGS],
- *ptr;
- int i;
-
- ptr = p;
- *ptr = '\0';
- for (i = 0; i < nargs; i++)
- {
- if (i)
- {
- *ptr++ = ',';
- *ptr++ = ' ';
- }
- if (argtypes[i] != 0)
- {
- strcpy(ptr, tname(get_id_type(argtypes[i])));
- *(ptr + NAMEDATALEN) = '\0';
- }
- else
- strcpy(ptr, "opaque");
- ptr += strlen(ptr);
- }
-
- elog(WARN, "%s: function %s(%s) does not exist", caller, funcname, p);
-}
-
-/*
- * Error message when aggregate lookup fails that gives details of the
- * basetype
- */
-void
-agg_error(char *caller, char *aggname, Oid basetypeID)
-{
-
- /*
- * basetypeID that is Invalid (zero) means aggregate over all types.
- * (count)
- */
-
- if (basetypeID == InvalidOid)
- {
- elog(WARN, "%s: aggregate '%s' for all types does not exist", caller, aggname);
- }
- else
- {
- elog(WARN, "%s: aggregate '%s' for '%s' does not exist", caller, aggname,
- tname(get_id_type(basetypeID)));
- }
-}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index abaedbb57b..d146b6e4ce 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.71 1997/11/24 16:55:22 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.72 1997/11/25 22:05:29 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -39,8 +39,6 @@
#include "nodes/parsenodes.h"
#include "nodes/print.h"
#include "parser/gramparse.h"
-#include "parser/catalog_utils.h"
-#include "parser/parse_query.h"
#include "utils/acl.h"
#include "catalog/catname.h"
#include "utils/elog.h"
@@ -49,8 +47,11 @@
static char saved_relname[NAMEDATALEN]; /* need this for complex attributes */
static bool QueryIsRule = FALSE;
static Node *saved_In_Expr;
+static Oid *param_type_info;
+static int pfunc_num_args;
extern List *parsetree;
+
/*
* If you need access to certain yacc-generated variables and find that
* they're static by default, uncomment the next line. (this is not a
@@ -64,6 +65,9 @@ static List *makeConstantList( A_Const *node);
static char *FlattenStringList(List *list);
static char *fmtId(char *rawid);
static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr);
+static void param_type_init(Oid *typev, int nargs);
+
+Oid param_type(int t); /* used in parse_expr.c */
/* old versions of flex define this as a macro */
#if defined(yywrap)
@@ -2324,7 +2328,7 @@ Typename: Array opt_array_bounds
* emp(name=text,mgr=emp)
*/
$$->setof = TRUE;
- else if (get_typrelid((Type)type($$->name)) != InvalidOid)
+ else if (typeTypeRelid(typenameType($$->name)) != InvalidOid)
/* (Eventually add in here that the set can only
* contain one element.)
*/
@@ -3690,4 +3694,24 @@ printf("fmtId- %sconvert %s to %s\n", ((cp == rawid)? "do not ": ""), rawid, cp)
#endif
return(cp);
-} /* fmtId() */
+}
+
+/*
+ * param_type_init()
+ *
+ * keep enough information around fill out the type of param nodes
+ * used in postquel functions
+ */
+static 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];
+}
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 0312e0251b..1c4b63c44e 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.24 1997/11/24 05:32:28 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.25 1997/11/25 22:05:32 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,8 +18,8 @@
#include "nodes/pg_list.h"
#include "nodes/parsenodes.h"
#include "parse.h"
-#include "utils/elog.h"
#include "parser/keywords.h"
+#include "utils/elog.h"
/*
* List of (keyword-name, keyword-token-value) pairs.
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
new file mode 100644
index 0000000000..b64b92079e
--- /dev/null
+++ b/src/backend/parser/parse_agg.c
@@ -0,0 +1,371 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_agg.c--
+ * handle aggregates in parser
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.1 1997/11/25 22:05:34 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "postgres.h"
+#include "access/heapam.h"
+#include "catalog/pg_aggregate.h"
+#include "nodes/nodeFuncs.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "optimizer/clauses.h"
+#include "parser/parse_agg.h"
+#include "parser/parse_node.h"
+#include "parser/parse_target.h"
+#include "utils/syscache.h"
+
+#ifdef 0
+#include "nodes/nodes.h"
+#include "nodes/params.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/acl.h"
+#include "nodes/makefuncs.h" /* for makeResdom(), etc. */
+#include "commands/sequence.h"
+
+#endif
+
+/*
+ * AddAggToParseState -
+ * add the aggregate to the list of unique aggregates in pstate.
+ *
+ * SIDE EFFECT: aggno in target list entry will be modified
+ */
+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.
+ */
+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.
+ */
+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.
+ */
+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;
+}
+
+/*
+ * tleIsAggOrGroupCol -
+ * returns true if the TargetEntry is Agg or GroupCol.
+ */
+bool
+tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause)
+{
+ 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;
+ }
+ }
+
+ if (IsA(expr, Aggreg))
+ return TRUE;
+
+ 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.
+ */
+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.
+ */
+/*
+ * 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;
+}
+
+
+Aggreg *
+ParseAgg(char *aggname, Oid basetype, Node *target)
+{
+ Oid fintype;
+ Oid vartype;
+ Oid xfn1;
+ Form_pg_aggregate aggform;
+ Aggreg *aggreg;
+ HeapTuple theAggTuple;
+
+ theAggTuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggname),
+ ObjectIdGetDatum(basetype),
+ 0, 0);
+ if (!HeapTupleIsValid(theAggTuple))
+ {
+ elog(WARN, "aggregate %s does not exist", aggname);
+ }
+
+ aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
+ fintype = aggform->aggfinaltype;
+ xfn1 = aggform->aggtransfn1;
+
+ if (nodeTag(target) != T_Var && nodeTag(target) != T_Expr)
+ elog(WARN, "parser: aggregate can only be applied on an attribute or expression");
+
+ /* only aggregates with transfn1 need a base type */
+ if (OidIsValid(xfn1))
+ {
+ basetype = aggform->aggbasetype;
+ if (nodeTag(target) == T_Var)
+ vartype = ((Var *) target)->vartype;
+ else
+ vartype = ((Expr *) target)->typeOid;
+
+ if (basetype != vartype)
+ {
+ Type tp1,
+ tp2;
+
+ tp1 = typeidType(basetype);
+ tp2 = typeidType(vartype);
+ elog(NOTICE, "Aggregate type mismatch:");
+ elog(WARN, "%s works on %s, not %s", aggname,
+ typeTypeName(tp1), typeTypeName(tp2));
+ }
+ }
+
+ aggreg = makeNode(Aggreg);
+ aggreg->aggname = pstrdup(aggname);
+ aggreg->basetype = aggform->aggbasetype;
+ aggreg->aggtype = fintype;
+
+ aggreg->target = target;
+
+ return aggreg;
+}
+
+/*
+ * Error message when aggregate lookup fails that gives details of the
+ * basetype
+ */
+void
+agg_error(char *caller, char *aggname, Oid basetypeID)
+{
+
+ /*
+ * basetypeID that is Invalid (zero) means aggregate over all types.
+ * (count)
+ */
+
+ if (basetypeID == InvalidOid)
+ {
+ elog(WARN, "%s: aggregate '%s' for all types does not exist", caller, aggname);
+ }
+ else
+ {
+ elog(WARN, "%s: aggregate '%s' for '%s' does not exist", caller, aggname,
+ typeidTypeName(basetypeID));
+ }
+}
+
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
new file mode 100644
index 0000000000..8e08e00a2e
--- /dev/null
+++ b/src/backend/parser/parse_clause.c
@@ -0,0 +1,407 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_clause.c--
+ * handle clauses in parser
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.1 1997/11/25 22:05:35 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "postgres.h"
+#include "access/heapam.h"
+#include "parser/parse_clause.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_node.h"
+#include "parser/parse_oper.h"
+#include "parser/parse_relation.h"
+#include "parser/parse_target.h"
+#include "catalog/pg_type.h"
+
+#ifdef 0
+#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_aggregate.h"
+#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 "nodes/makefuncs.h" /* for makeResdom(), etc. */
+#include "nodes/nodeFuncs.h"
+#include "commands/sequence.h"
+
+#include "optimizer/clauses.h"
+
+#include "miscadmin.h"
+
+#include "port-protos.h" /* strdup() */
+#endif
+
+/*
+ * 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)
+ *
+ */
+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);
+ }
+}
+
+/*
+ * makeRangeTable -
+ * make a range table with the specified relation (optional) and the
+ * from-clause.
+ */
+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);
+ 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);
+ /* will close relation later */
+}
+
+/*****************************************************************************
+ *
+ * Where Clause
+ *
+ *****************************************************************************/
+
+/*
+ * transformWhereClause -
+ * transforms the qualification and make sure it is of type Boolean
+ *
+ */
+Node *
+transformWhereClause(ParseState *pstate, Node *a_expr)
+{
+ Node *qual;
+
+ if (a_expr == NULL)
+ return (Node *) NULL; /* no qualifiers */
+
+ pstate->p_in_where_clause = true;
+ qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST);
+ pstate->p_in_where_clause = false;
+ if (exprType(qual) != BOOLOID)
+ {
+ elog(WARN,
+ "where clause must return type bool, not %s",
+ typeidTypeName(exprType(qual)));
+ }
+ return qual;
+}
+
+/*****************************************************************************
+ *
+ * Sort Clause
+ *
+ *****************************************************************************/
+
+/*
+ * find_targetlist_entry -
+ * returns the Resdom in the target list matching the specified varname
+ * and range
+ *
+ */
+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;
+}
+
+/*
+ * transformGroupClause -
+ * transform a Group By clause
+ *
+ */
+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
+ {
+ List *i;
+
+ foreach (i, glist)
+ {
+ GroupClause *gcl = (GroupClause *) lfirst (i);
+
+ if ( gcl->entry == grpcl->entry )
+ break;
+ }
+ if ( i == NIL ) /* not in grouplist already */
+ {
+ lnext(gl) = lcons(grpcl, NIL);
+ gl = lnext(gl);
+ }
+ else
+ pfree (grpcl); /* get rid of this */
+ }
+ grouplist = lnext(grouplist);
+ }
+
+ return glist;
+}
+
+/*
+ * transformSortClause -
+ * transform an Order By clause
+ *
+ */
+List *
+transformSortClause(ParseState *pstate,
+ List *orderlist, List *targetlist,
+ char *uniqueFlag)
+{
+ List *sortlist = NIL;
+ List *s = NIL;
+
+ 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
+ {
+ List *i;
+
+ foreach (i, sortlist)
+ {
+ SortClause *scl = (SortClause *) lfirst (i);
+
+ if ( scl->resdom == sortcl->resdom )
+ break;
+ }
+ if ( i == NIL ) /* not in sortlist already */
+ {
+ lnext(s) = lcons(sortcl, NIL);
+ s = lnext(s);
+ }
+ else
+ pfree (sortcl); /* get rid of this */
+ }
+ orderlist = lnext(orderlist);
+ }
+
+ if (uniqueFlag)
+ {
+ List *i;
+
+ 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;
+}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
new file mode 100644
index 0000000000..fe00b2b48a
--- /dev/null
+++ b/src/backend/parser/parse_expr.c
@@ -0,0 +1,694 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_expr.c
+ * handle expressions in parser
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.1 1997/11/25 22:05:39 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "postgres.h"
+#include "catalog/pg_type.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodes.h"
+#include "nodes/params.h"
+#include "nodes/relation.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_func.h"
+#include "parser/parse_node.h"
+#include "parser/parse_relation.h"
+#include "parser/parse_target.h"
+#include "parse.h"
+#include "utils/builtins.h"
+
+#ifdef 0
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_proc.h"
+#include "utils/elog.h"
+#include "utils/lsyscache.h"
+#include "utils/palloc.h"
+#include "utils/mcxt.h"
+#include "utils/syscache.h"
+#include "utils/acl.h"
+#include "nodes/nodeFuncs.h"
+#include "commands/sequence.h"
+
+#include "optimizer/clauses.h"
+#include "access/heapam.h"
+
+#include "miscadmin.h"
+#endif
+
+Oid param_type(int t); /* from gram.y */
+
+/*
+ * 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.
+ */
+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",
+ typeidTypeName(exprType(lexpr)));
+ if (exprType(rexpr) != BOOLOID)
+ elog(WARN,
+ "right-hand side of AND is type '%s', not bool",
+ typeidTypeName(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",
+ typeidTypeName(exprType(lexpr)));
+ if (exprType(rexpr) != BOOLOID)
+ elog(WARN,
+ "right-hand side of OR is type '%s', not bool",
+ typeidTypeName(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",
+ typeidTypeName(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;
+}
+
+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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ ** HandleNestedDots --
+ ** Given a nested dot expression (i.e. (relation func ... attr), build up
+ ** a tree with of Iter and Func nodes.
+ */
+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);
+}
+
+Node *
+parser_typecast(Value *expr, TypeName *typename, int typlen)
+{
+ /* check for passing non-ints */
+ Const *adt;
+ Datum lcp;
+ Type tp;
+ char type_string[NAMEDATALEN];
+ int32 len;
+ char *cp = NULL;
+ char *const_string = NULL;
+ bool string_palloced = false;
+
+ switch (nodeTag(expr))
+ {
+ case T_String:
+ const_string = DatumGetPointer(expr->val.str);
+ break;
+ case T_Integer:
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%ld", expr->val.ival);
+ break;
+ default:
+ elog(WARN,
+ "parser_typecast: cannot cast this expression to type \"%s\"",
+ typename->name);
+ }
+
+ if (typename->arrayBounds != NIL)
+ {
+ sprintf(type_string, "_%s", typename->name);
+ tp = (Type) typenameType(type_string);
+ }
+ else
+ {
+ tp = (Type) typenameType(typename->name);
+ }
+
+ len = typeLen(tp);
+
+#if 0 /* fix me */
+ switch (CInteger(lfirst(expr)))
+ {
+ case INT4OID: /* int4 */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%d", ((Const *) lnext(expr))->constvalue);
+ break;
+
+ case NAMEOID: /* char16 */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%s", ((Const *) lnext(expr))->constvalue);
+ break;
+
+ case CHAROID: /* char */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%c", ((Const) lnext(expr))->constvalue);
+ break;
+
+ case FLOAT8OID: /* float8 */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%f", ((Const) lnext(expr))->constvalue);
+ break;
+
+ case CASHOID: /* money */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%d",
+ (int) ((Const *) expr)->constvalue);
+ break;
+
+ case TEXTOID: /* text */
+ const_string = DatumGetPointer(((Const) lnext(expr))->constvalue);
+ const_string = (char *) textout((struct varlena *) const_string);
+ break;
+
+ case UNKNOWNOID: /* unknown */
+ const_string = DatumGetPointer(((Const) lnext(expr))->constvalue);
+ const_string = (char *) textout((struct varlena *) const_string);
+ break;
+
+ default:
+ elog(WARN, "unknown type %d", CInteger(lfirst(expr)));
+ }
+#endif
+
+ cp = stringTypeString(tp, const_string, typlen);
+
+ if (!typeByVal(tp))
+ {
+/*
+ if (len >= 0 && len != PSIZE(cp)) {
+ char *pp;
+ pp = (char *) palloc(len);
+ memmove(pp, cp, len);
+ cp = pp;
+ }
+*/
+ lcp = PointerGetDatum(cp);
+ }
+ else
+ {
+ switch (len)
+ {
+ case 1:
+ lcp = Int8GetDatum(cp);
+ break;
+ case 2:
+ lcp = Int16GetDatum(cp);
+ break;
+ case 4:
+ lcp = Int32GetDatum(cp);
+ break;
+ default:
+ lcp = PointerGetDatum(cp);
+ break;
+ }
+ }
+
+ adt = makeConst(typeTypeId(tp),
+ len,
+ (Datum) lcp,
+ false,
+ typeByVal(tp),
+ false, /* not a set */
+ true /* is cast */ );
+
+ if (string_palloced)
+ pfree(const_string);
+
+ return (Node *) adt;
+}
+
+Node *
+parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen)
+{
+ /* check for passing non-ints */
+ Const *adt;
+ Datum lcp;
+ int32 len = typeLen(tp);
+ char *cp = NULL;
+
+ char *const_string = NULL;
+ bool string_palloced = false;
+
+ Assert(IsA(expr, Const));
+
+ switch (exprType)
+ {
+ case 0: /* NULL */
+ break;
+ case INT4OID: /* int4 */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%d",
+ (int) ((Const *) expr)->constvalue);
+ break;
+ case NAMEOID: /* char16 */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%s",
+ (char *) ((Const *) expr)->constvalue);
+ break;
+ case CHAROID: /* char */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%c",
+ (char) ((Const *) expr)->constvalue);
+ break;
+ case FLOAT4OID: /* float4 */
+ {
+ float32 floatVal =
+ DatumGetFloat32(((Const *) expr)->constvalue);
+
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%f", *floatVal);
+ break;
+ }
+ case FLOAT8OID: /* float8 */
+ {
+ float64 floatVal =
+ DatumGetFloat64(((Const *) expr)->constvalue);
+
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%f", *floatVal);
+ break;
+ }
+ case CASHOID: /* money */
+ const_string = (char *) palloc(256);
+ string_palloced = true;
+ sprintf(const_string, "%ld",
+ (long) ((Const *) expr)->constvalue);
+ break;
+ case TEXTOID: /* text */
+ const_string =
+ DatumGetPointer(((Const *) expr)->constvalue);
+ const_string = (char *) textout((struct varlena *) const_string);
+ break;
+ case UNKNOWNOID: /* unknown */
+ const_string =
+ DatumGetPointer(((Const *) expr)->constvalue);
+ const_string = (char *) textout((struct varlena *) const_string);
+ break;
+ default:
+ elog(WARN, "unknown type %u ", exprType);
+ }
+
+ if (!exprType)
+ {
+ adt = makeConst(typeTypeId(tp),
+ (Size) 0,
+ (Datum) NULL,
+ true, /* isnull */
+ false, /* was omitted */
+ false, /* not a set */
+ true /* is cast */ );
+ return ((Node *) adt);
+ }
+
+ cp = stringTypeString(tp, const_string, typlen);
+
+
+ if (!typeByVal(tp))
+ {
+/*
+ if (len >= 0 && len != PSIZE(cp)) {
+ char *pp;
+ pp = (char *) palloc(len);
+ memmove(pp, cp, len);
+ cp = pp;
+ }
+*/
+ lcp = PointerGetDatum(cp);
+ }
+ else
+ {
+ switch (len)
+ {
+ case 1:
+ lcp = Int8GetDatum(cp);
+ break;
+ case 2:
+ lcp = Int16GetDatum(cp);
+ break;
+ case 4:
+ lcp = Int32GetDatum(cp);
+ break;
+ default:
+ lcp = PointerGetDatum(cp);
+ break;
+ }
+ }
+
+ adt = makeConst(typeTypeId(tp),
+ (Size) len,
+ (Datum) lcp,
+ false,
+ false, /* was omitted */
+ false, /* not a set */
+ true /* is cast */ );
+
+ /*
+ * printf("adt %s : %u %d %d\n",CString(expr),typeTypeId(tp) , len,cp);
+ */
+ if (string_palloced)
+ pfree(const_string);
+
+ return ((Node *) adt);
+}
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
new file mode 100644
index 0000000000..bb2a7773fd
--- /dev/null
+++ b/src/backend/parser/parse_func.c
@@ -0,0 +1,1264 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_func.c
+ * handle function calls in parser
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.1 1997/11/25 22:05:41 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/itup.h"
+#include "access/relscan.h"
+#include "access/sdir.h"
+#include "catalog/catname.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "lib/dllist.h"
+#include "nodes/makefuncs.h"
+#include "nodes/relation.h"
+#include "parser/parse_agg.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_func.h"
+#include "parser/parse_node.h"
+#include "parser/parse_relation.h"
+#include "parser/parse_type.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+#ifdef 0
+#include "utils/datum.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+
+#include "catalog/pg_operator.h"
+#include "catalog/catname.h"
+
+#include "access/skey.h"
+#include "access/tupdesc.h"
+#include "access/htup.h"
+#include "access/genam.h"
+#include "access/itup.h"
+#include "access/tupmacs.h"
+
+#include "storage/buf.h"
+#endif
+
+#define ISCOMPLEX(type) (typeidTypeRelid(type) ? true : false)
+
+#define MAXFARGS 8 /* max # args to a c or postquel function */
+
+typedef struct _SuperQE
+{
+ Oid sqe_relid;
+} SuperQE;
+
+/*
+ * parse function
+ */
+
+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);
+
+ 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(typeidTypeName(toid));
+ if (RelationIsValid(rd))
+ {
+ relname = RelationGetRelationName(rd)->data;
+ heap_close(rd);
+ }
+ else
+ elog(WARN,
+ "Type '%s' is not a relation type",
+ typeidTypeName(toid));
+ argrelid = typeidTypeRelid(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.
+ */
+ 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);
+ 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 = typeTypeId(typenameType(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 (typeidTypeRelid(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 = attnameTypeId(argrelid, funcname);
+ }
+ }
+
+ /*
+ * Sequence handling.
+ */
+ if (funcid == SeqNextValueRegProcedure ||
+ funcid == SeqCurrValueRegProcedure)
+ {
+ Const *seq;
+ char *seqrel;
+ text *seqname;
+ int32 aclcheck_result = -1;
+ 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);
+ 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 && pstate->p_in_where_clause)
+ 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);
+}
+
+Oid
+funcid_get_rettype(Oid funcid)
+{
+ HeapTuple func_tuple = NULL;
+ Oid funcrettype = (Oid) 0;
+
+ func_tuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(func_tuple))
+ elog(WARN, "function %d does not exist", funcid);
+
+ funcrettype = (Oid)
+ ((Form_pg_proc) GETSTRUCT(func_tuple))->prorettype;
+
+ return (funcrettype);
+}
+
+/*
+ * get a list of all argument type vectors for which a function named
+ * funcname taking nargs arguments exists
+ */
+CandidateList
+func_get_candidates(char *funcname, int nargs)
+{
+ Relation heapRelation;
+ Relation idesc;
+ ScanKeyData skey;
+ HeapTuple tuple;
+ IndexScanDesc sd;
+ RetrieveIndexResult indexRes;
+ Buffer buffer;
+ Form_pg_proc pgProcP;
+ bool bufferUsed = FALSE;
+ CandidateList candidates = NULL;
+ CandidateList current_candidate;
+ int i;
+
+ heapRelation = heap_openr(ProcedureRelationName);
+ ScanKeyEntryInitialize(&skey,
+ (bits16) 0x0,
+ (AttrNumber) 1,
+ (RegProcedure) NameEqualRegProcedure,
+ (Datum) funcname);
+
+ idesc = index_openr(ProcedureNameIndex);
+
+ sd = index_beginscan(idesc, false, 1, &skey);
+
+ do
+ {
+ tuple = (HeapTuple) NULL;
+ if (bufferUsed)
+ {
+ ReleaseBuffer(buffer);
+ bufferUsed = FALSE;
+ }
+
+ indexRes = index_getnext(sd, ForwardScanDirection);
+ if (indexRes)
+ {
+ ItemPointer iptr;
+
+ iptr = &indexRes->heap_iptr;
+ tuple = heap_fetch(heapRelation, false, iptr, &buffer);
+ pfree(indexRes);
+ if (HeapTupleIsValid(tuple))
+ {
+ pgProcP = (Form_pg_proc) GETSTRUCT(tuple);
+ bufferUsed = TRUE;
+ if (pgProcP->pronargs == nargs)
+ {
+ current_candidate = (CandidateList)
+ palloc(sizeof(struct _CandidateList));
+ current_candidate->args = (Oid *)
+ palloc(8 * sizeof(Oid));
+ MemSet(current_candidate->args, 0, 8 * sizeof(Oid));
+ for (i = 0; i < nargs; i++)
+ {
+ current_candidate->args[i] =
+ pgProcP->proargtypes[i];
+ }
+
+ current_candidate->next = candidates;
+ candidates = current_candidate;
+ }
+ }
+ }
+ } while (indexRes);
+
+ index_endscan(sd);
+ index_close(idesc);
+ heap_close(heapRelation);
+
+ return candidates;
+}
+
+/*
+ * can input_typeids be coerced to func_typeids?
+ */
+bool
+can_coerce(int nargs, Oid *input_typeids, Oid *func_typeids)
+{
+ int i;
+ Type tp;
+
+ /*
+ * right now, we only coerce "unknown", and we cannot coerce it to a
+ * relation type
+ */
+ for (i = 0; i < nargs; i++)
+ {
+ if (input_typeids[i] != func_typeids[i])
+ {
+ if ((input_typeids[i] == BPCHAROID && func_typeids[i] == TEXTOID) ||
+ (input_typeids[i] == BPCHAROID && func_typeids[i] == VARCHAROID) ||
+ (input_typeids[i] == VARCHAROID && func_typeids[i] == TEXTOID) ||
+ (input_typeids[i] == VARCHAROID && func_typeids[i] == BPCHAROID) ||
+ (input_typeids[i] == CASHOID && func_typeids[i] == INT4OID) ||
+ (input_typeids[i] == INT4OID && func_typeids[i] == CASHOID))
+ ; /* these are OK */
+ else if (input_typeids[i] != UNKNOWNOID || func_typeids[i] == 0)
+ return false;
+
+ tp = typeidType(input_typeids[i]);
+ if (typeTypeFlag(tp) == 'c')
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * given a list of possible typeid arrays to a function and an array of
+ * input typeids, produce a shortlist of those function typeid arrays
+ * that match the input typeids (either exactly or by coercion), and
+ * return the number of such arrays
+ */
+int
+match_argtypes(int nargs,
+ Oid *input_typeids,
+ CandidateList function_typeids,
+ CandidateList *candidates) /* return value */
+{
+ CandidateList current_candidate;
+ CandidateList matching_candidate;
+ Oid *current_typeids;
+ int ncandidates = 0;
+
+ *candidates = NULL;
+
+ for (current_candidate = function_typeids;
+ current_candidate != NULL;
+ current_candidate = current_candidate->next)
+ {
+ current_typeids = current_candidate->args;
+ if (can_coerce(nargs, input_typeids, current_typeids))
+ {
+ matching_candidate = (CandidateList)
+ palloc(sizeof(struct _CandidateList));
+ matching_candidate->args = current_typeids;
+ matching_candidate->next = *candidates;
+ *candidates = matching_candidate;
+ ncandidates++;
+ }
+ }
+
+ return ncandidates;
+}
+
+/*
+ * given the input argtype array and more than one candidate
+ * for the function argtype array, attempt to resolve the conflict.
+ * returns the selected argtype array if the conflict can be resolved,
+ * otherwise returns NULL
+ */
+Oid *
+func_select_candidate(int nargs,
+ Oid *input_typeids,
+ CandidateList candidates)
+{
+ /* XXX no conflict resolution implemeneted yet */
+ return (NULL);
+}
+
+bool
+func_get_detail(char *funcname,
+ int nargs,
+ Oid *oid_array,
+ Oid *funcid, /* return value */
+ Oid *rettype, /* return value */
+ bool *retset, /* return value */
+ Oid **true_typeids) /* return value */
+{
+ Oid **input_typeid_vector;
+ Oid *current_input_typeids;
+ CandidateList function_typeids;
+ CandidateList current_function_typeids;
+ HeapTuple ftup;
+ Form_pg_proc pform;
+
+ /*
+ * attempt to find named function in the system catalogs with
+ * arguments exactly as specified - so that the normal case is just as
+ * quick as before
+ */
+ ftup = SearchSysCacheTuple(PRONAME,
+ PointerGetDatum(funcname),
+ Int32GetDatum(nargs),
+ PointerGetDatum(oid_array),
+ 0);
+ *true_typeids = oid_array;
+
+ /*
+ * If an exact match isn't found : 1) get a vector of all possible
+ * input arg type arrays constructed from the superclasses of the
+ * original input arg types 2) get a list of all possible argument
+ * type arrays to the function with given name and number of arguments
+ * 3) for each input arg type array from vector #1 : a) find how many
+ * of the function arg type arrays from list #2 it can be coerced to
+ * b) - if the answer is one, we have our function - if the answer is
+ * more than one, attempt to resolve the conflict - if the answer is
+ * zero, try the next array from vector #1
+ */
+ if (!HeapTupleIsValid(ftup))
+ {
+ function_typeids = func_get_candidates(funcname, nargs);
+
+ if (function_typeids != NULL)
+ {
+ int ncandidates = 0;
+
+ input_typeid_vector = argtype_inherit(nargs, oid_array);
+ current_input_typeids = oid_array;
+
+ do
+ {
+ ncandidates = match_argtypes(nargs, current_input_typeids,
+ function_typeids,
+ &current_function_typeids);
+ if (ncandidates == 1)
+ {
+ *true_typeids = current_function_typeids->args;
+ ftup = SearchSysCacheTuple(PRONAME,
+ PointerGetDatum(funcname),
+ Int32GetDatum(nargs),
+ PointerGetDatum(*true_typeids),
+ 0);
+ Assert(HeapTupleIsValid(ftup));
+ }
+ else if (ncandidates > 1)
+ {
+ *true_typeids =
+ func_select_candidate(nargs,
+ current_input_typeids,
+ current_function_typeids);
+ if (*true_typeids == NULL)
+ {
+ elog(NOTICE, "there is more than one function named \"%s\"",
+ funcname);
+ elog(NOTICE, "that satisfies the given argument types. you will have to");
+ elog(NOTICE, "retype your query using explicit typecasts.");
+ func_error("func_get_detail", funcname, nargs, oid_array);
+ }
+ else
+ {
+ ftup = SearchSysCacheTuple(PRONAME,
+ PointerGetDatum(funcname),
+ Int32GetDatum(nargs),
+ PointerGetDatum(*true_typeids),
+ 0);
+ Assert(HeapTupleIsValid(ftup));
+ }
+ }
+ current_input_typeids = *input_typeid_vector++;
+ }
+ while (current_input_typeids !=
+ InvalidOid && ncandidates == 0);
+ }
+ }
+
+ if (!HeapTupleIsValid(ftup))
+ {
+ Type tp;
+
+ if (nargs == 1)
+ {
+ tp = typeidType(oid_array[0]);
+ if (typeTypeFlag(tp) == 'c')
+ elog(WARN, "no such attribute or function \"%s\"",
+ funcname);
+ }
+ func_error("func_get_detail", funcname, nargs, oid_array);
+ }
+ else
+ {
+ pform = (Form_pg_proc) GETSTRUCT(ftup);
+ *funcid = ftup->t_oid;
+ *rettype = pform->prorettype;
+ *retset = pform->proretset;
+
+ return (true);
+ }
+/* shouldn't reach here */
+ return (false);
+
+}
+
+/*
+ * argtype_inherit() -- Construct an argtype vector reflecting the
+ * inheritance properties of the supplied argv.
+ *
+ * This function is used to disambiguate among functions with the
+ * same name but different signatures. It takes an array of eight
+ * type ids. For each type id in the array that's a complex type
+ * (a class), it walks up the inheritance tree, finding all
+ * superclasses of that type. A vector of new Oid type arrays
+ * is returned to the caller, reflecting the structure of the
+ * inheritance tree above the supplied arguments.
+ *
+ * The order of this vector is as follows: all superclasses of the
+ * rightmost complex class are explored first. The exploration
+ * continues from right to left. This policy means that we favor
+ * keeping the leftmost argument type as low in the inheritance tree
+ * as possible. This is intentional; it is exactly what we need to
+ * do for method dispatch. The last type array we return is all
+ * zeroes. This will match any functions for which return types are
+ * not defined. There are lots of these (mostly builtins) in the
+ * catalogs.
+ */
+Oid **
+argtype_inherit(int nargs, Oid *oid_array)
+{
+ Oid relid;
+ int i;
+ InhPaths arginh[MAXFARGS];
+
+ for (i = 0; i < MAXFARGS; i++)
+ {
+ if (i < nargs)
+ {
+ arginh[i].self = oid_array[i];
+ if ((relid = typeidTypeRelid(oid_array[i])) != InvalidOid)
+ {
+ arginh[i].nsupers = findsupers(relid, &(arginh[i].supervec));
+ }
+ else
+ {
+ arginh[i].nsupers = 0;
+ arginh[i].supervec = (Oid *) NULL;
+ }
+ }
+ else
+ {
+ arginh[i].self = InvalidOid;
+ arginh[i].nsupers = 0;
+ arginh[i].supervec = (Oid *) NULL;
+ }
+ }
+
+ /* return an ordered cross-product of the classes involved */
+ return (genxprod(arginh, nargs));
+}
+
+int findsupers(Oid relid, Oid **supervec)
+{
+ Oid *relidvec;
+ Relation inhrel;
+ HeapScanDesc inhscan;
+ ScanKeyData skey;
+ HeapTuple inhtup;
+ TupleDesc inhtupdesc;
+ int nvisited;
+ SuperQE *qentry,
+ *vnode;
+ Dllist *visited,
+ *queue;
+ Dlelem *qe,
+ *elt;
+
+ Relation rd;
+ Buffer buf;
+ Datum d;
+ bool newrelid;
+ char isNull;
+
+ nvisited = 0;
+ queue = DLNewList();
+ visited = DLNewList();
+
+
+ inhrel = heap_openr(InheritsRelationName);
+ RelationSetLockForRead(inhrel);
+ inhtupdesc = RelationGetTupleDescriptor(inhrel);
+
+ /*
+ * Use queue to do a breadth-first traversal of the inheritance graph
+ * from the relid supplied up to the root.
+ */
+ do
+ {
+ ScanKeyEntryInitialize(&skey, 0x0, Anum_pg_inherits_inhrel,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(relid));
+
+ inhscan = heap_beginscan(inhrel, 0, false, 1, &skey);
+
+ while (HeapTupleIsValid(inhtup = heap_getnext(inhscan, 0, &buf)))
+ {
+ qentry = (SuperQE *) palloc(sizeof(SuperQE));
+
+ d = fastgetattr(inhtup, Anum_pg_inherits_inhparent,
+ inhtupdesc, &isNull);
+ qentry->sqe_relid = DatumGetObjectId(d);
+
+ /* put this one on the queue */
+ DLAddTail(queue, DLNewElem(qentry));
+
+ ReleaseBuffer(buf);
+ }
+
+ heap_endscan(inhscan);
+
+ /* pull next unvisited relid off the queue */
+ do
+ {
+ qe = DLRemHead(queue);
+ qentry = qe ? (SuperQE *) DLE_VAL(qe) : NULL;
+
+ if (qentry == (SuperQE *) NULL)
+ break;
+
+ relid = qentry->sqe_relid;
+ newrelid = true;
+
+ for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt))
+ {
+ vnode = (SuperQE *) DLE_VAL(elt);
+ if (vnode && (qentry->sqe_relid == vnode->sqe_relid))
+ {
+ newrelid = false;
+ break;
+ }
+ }
+ } while (!newrelid);
+
+ if (qentry != (SuperQE *) NULL)
+ {
+
+ /* save the type id, rather than the relation id */
+ if ((rd = heap_open(qentry->sqe_relid)) == (Relation) NULL)
+ elog(WARN, "relid %d does not exist", qentry->sqe_relid);
+ qentry->sqe_relid = typeTypeId(typenameType(RelationGetRelationName(rd)->data));
+ heap_close(rd);
+
+ DLAddTail(visited, qe);
+
+ nvisited++;
+ }
+ } while (qentry != (SuperQE *) NULL);
+
+ RelationUnsetLockForRead(inhrel);
+ heap_close(inhrel);
+
+ if (nvisited > 0)
+ {
+ relidvec = (Oid *) palloc(nvisited * sizeof(Oid));
+ *supervec = relidvec;
+
+ for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt))
+ {
+ vnode = (SuperQE *) DLE_VAL(elt);
+ *relidvec++ = vnode->sqe_relid;
+ }
+
+ }
+ else
+ {
+ *supervec = (Oid *) NULL;
+ }
+
+ return (nvisited);
+}
+
+Oid **
+genxprod(InhPaths *arginh, int nargs)
+{
+ int nanswers;
+ Oid **result,
+ **iter;
+ Oid *oneres;
+ int i,
+ j;
+ int cur[MAXFARGS];
+
+ nanswers = 1;
+ for (i = 0; i < nargs; i++)
+ {
+ nanswers *= (arginh[i].nsupers + 2);
+ cur[i] = 0;
+ }
+
+ iter = result = (Oid **) palloc(sizeof(Oid *) * nanswers);
+
+ /* compute the cross product from right to left */
+ for (;;)
+ {
+ oneres = (Oid *) palloc(MAXFARGS * sizeof(Oid));
+ MemSet(oneres, 0, MAXFARGS * sizeof(Oid));
+
+ for (i = nargs - 1; i >= 0 && cur[i] > arginh[i].nsupers; i--)
+ continue;
+
+ /* if we're done, terminate with NULL pointer */
+ if (i < 0)
+ {
+ *iter = NULL;
+ return (result);
+ }
+
+ /* no, increment this column and zero the ones after it */
+ cur[i] = cur[i] + 1;
+ for (j = nargs - 1; j > i; j--)
+ cur[j] = 0;
+
+ for (i = 0; i < nargs; i++)
+ {
+ if (cur[i] == 0)
+ oneres[i] = arginh[i].self;
+ else if (cur[i] > arginh[i].nsupers)
+ oneres[i] = 0; /* wild card */
+ else
+ oneres[i] = arginh[i].supervec[cur[i] - 1];
+ }
+
+ *iter++ = oneres;
+ }
+}
+
+
+/*
+ ** make_arguments --
+ ** Given the number and types of arguments to a function, and the
+ ** actual arguments and argument types, do the necessary typecasting.
+ */
+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],
+ typeidType(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.
+ */
+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 = attnameTypeId(relid, attname);
+ resnode = makeResdom(1,
+ typeid,
+ typeLen(typeidType(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.
+ */
+List *
+setup_base_tlist(Oid typeid)
+{
+ TargetEntry *tle;
+ Resdom *resnode;
+ Var *varnode;
+
+ resnode = makeResdom(1,
+ typeid,
+ typeLen(typeidType(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).
+ */
+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 = typeidTypeRelid(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(typeidTypeName(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 = attnumTypeId(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 = typeidTypeRelid(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(typeidTypeName(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 = attnumTypeId(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(typeidTypeName(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 = attnumTypeId(rd, attnum);
+ param->param_tlist = setup_tlist(funcname, relid);
+ return ((Node *) param);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+/*
+ * Error message when function lookup fails that gives details of the
+ * argument types
+ */
+void
+func_error(char *caller, char *funcname, int nargs, Oid *argtypes)
+{
+ char p[(NAMEDATALEN + 2) * MAXFMGRARGS],
+ *ptr;
+ int i;
+
+ ptr = p;
+ *ptr = '\0';
+ for (i = 0; i < nargs; i++)
+ {
+ if (i)
+ {
+ *ptr++ = ',';
+ *ptr++ = ' ';
+ }
+ if (argtypes[i] != 0)
+ {
+ strcpy(ptr, typeidTypeName(argtypes[i]));
+ *(ptr + NAMEDATALEN) = '\0';
+ }
+ else
+ strcpy(ptr, "opaque");
+ ptr += strlen(ptr);
+ }
+
+ elog(WARN, "%s: function %s(%s) does not exist", caller, funcname, p);
+}
+
+
+
diff --git a/src/backend/parser/parse_query.c b/src/backend/parser/parse_node.c
index c47feeaa11..c06e00888b 100644
--- a/src/backend/parser/parse_query.c
+++ b/src/backend/parser/parse_node.c
@@ -1,259 +1,76 @@
/*-------------------------------------------------------------------------
*
- * parse_query.c--
- * take an "optimizable" stmt and make the query tree that
- * the planner requires.
+ * parse_node.c--
+ * various routines that make nodes for query plans
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.25 1997/11/24 05:08:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.1 1997/11/25 22:05:42 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <ctype.h>
#include <string.h>
-#include "postgres.h"
+#include "postgres.h"
#include "fmgr.h"
#include "access/heapam.h"
-#include "access/tupmacs.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_type.h"
+#include "nodes/makefuncs.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_oper.h"
+#include "parser/parse_node.h"
+#include "parser/parse_relation.h"
+#include "parser/parse_type.h"
#include "utils/builtins.h"
+#include "utils/syscache.h"
+
+#ifdef 0
+#include "access/tupmacs.h"
#include "utils/elog.h"
#include "utils/palloc.h"
#include "utils/acl.h" /* for ACL_NO_PRIV_WARNING */
#include "utils/rel.h" /* Relation stuff */
#include "utils/syscache.h"
-#include "catalog/pg_type.h"
-#include "catalog/pg_operator.h"
-#include "parser/catalog_utils.h"
-#include "parser/parse_query.h"
-#include "utils/lsyscache.h"
#include "nodes/pg_list.h"
#include "nodes/primnodes.h"
#include "nodes/parsenodes.h"
-#include "nodes/makefuncs.h"
-
-static void
-checkTargetTypes(ParseState *pstate, char *target_colname,
- char *refname, char *colname);
-
-Oid *param_type_info;
-int pfunc_num_args;
-
-/* given refname, return a pointer to the range table entry */
-RangeTblEntry *
-refnameRangeTableEntry(List *rtable, char *refname)
-{
- List *temp;
-
- foreach(temp, rtable)
- {
- RangeTblEntry *rte = lfirst(temp);
-
- if (!strcmp(rte->refname, refname))
- return rte;
- }
- return NULL;
-}
-
-/* given refname, return id of variable; position starts with 1 */
-int
-refnameRangeTablePosn(List *rtable, char *refname)
-{
- int index;
- List *temp;
-
- index = 1;
- foreach(temp, rtable)
- {
- RangeTblEntry *rte = lfirst(temp);
-
- if (!strcmp(rte->refname, refname))
- return index;
- index++;
- }
- return (0);
-}
+#endif
/*
- * returns range entry if found, else NULL
+ * make_parsestate() --
+ * allocate and initialize a new ParseState.
+ * the CALLERS is responsible for freeing the ParseState* returned
+ *
*/
-RangeTblEntry *
-colnameRangeTableEntry(ParseState *pstate, char *colname)
-{
- List *et;
- List *rtable;
- RangeTblEntry *rte_result;
-
- if (pstate->p_is_rule)
- rtable = lnext(lnext(pstate->p_rtable));
- else
- rtable = pstate->p_rtable;
-
- rte_result = NULL;
- foreach(et, rtable)
- {
- RangeTblEntry *rte = lfirst(et);
-
- /* only entries on outer(non-function?) scope */
- if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
- continue;
-
- if (get_attnum(rte->relid, colname) != InvalidAttrNumber)
- {
- if (rte_result != NULL)
- {
- if (!pstate->p_is_insert ||
- rte != pstate->p_target_rangetblentry)
- elog(WARN, "Column %s is ambiguous", colname);
- }
- else
- rte_result = rte;
- }
- }
- return rte_result;
-}
-/*
- * put new entry in pstate p_rtable structure, or return pointer
- * if pstate null
-*/
-RangeTblEntry *
-addRangeTableEntry(ParseState *pstate,
- char *relname,
- char *refname,
- bool inh,
- bool inFromCl)
-{
- Relation relation;
- RangeTblEntry *rte = makeNode(RangeTblEntry);
-
- if (pstate != NULL &&
- refnameRangeTableEntry(pstate->p_rtable, refname) != NULL)
- elog(WARN, "Table name %s specified more than once", refname);
-
- rte->relname = pstrdup(relname);
- rte->refname = pstrdup(refname);
-
- relation = heap_openr(relname);
- if (relation == NULL)
- {
- elog(WARN, "%s: %s",
- relname, aclcheck_error_strings[ACLCHECK_NO_CLASS]);
- }
-
- /*
- * Flags - zero or more from inheritance,union,version or
- * recursive (transitive closure) [we don't support them all -- ay
- * 9/94 ]
- */
- rte->inh = inh;
-
- /* RelOID */
- rte->relid = RelationGetRelationId(relation);
-
- rte->inFromCl = inFromCl;
-
- /*
- * close the relation we're done with it for now.
- */
- if (pstate != NULL)
- pstate->p_rtable = lappend(pstate->p_rtable, rte);
-
- heap_close(relation);
-
- return rte;
-}
-
-/*
- * expandAll -
- * makes a list of attributes
- * assumes reldesc caching works
- */
-List *
-expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
+ParseState *
+make_parsestate(void)
{
- Relation rdesc;
- List *te_tail = NIL,
- *te_head = NIL;
- Var *varnode;
- int varattno,
- maxattrs;
- Oid type_id;
- int type_len;
- RangeTblEntry *rte;
-
- rte = refnameRangeTableEntry(pstate->p_rtable, refname);
- if (rte == NULL)
- rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE);
-
- rdesc = heap_open(rte->relid);
-
- if (rdesc == NULL)
- {
- elog(WARN, "Unable to expand all -- heap_open failed on %s",
- rte->refname);
- return NIL;
- }
- maxattrs = RelationGetNumberOfAttributes(rdesc);
-
- for (varattno = 0; varattno <= maxattrs - 1; varattno++)
- {
- char *attrname;
- char *resname = NULL;
- TargetEntry *te = makeNode(TargetEntry);
-
- attrname = pstrdup((rdesc->rd_att->attrs[varattno]->attname).data);
- varnode = (Var *) make_var(pstate, refname, attrname, &type_id);
- type_len = (int) tlen(get_id_type(type_id));
-
- handleTargetColname(pstate, &resname, refname, attrname);
- if (resname != NULL)
- attrname = resname;
-
- /*
- * Even if the elements making up a set are complex, the set
- * itself is not.
- */
-
- te->resdom = makeResdom((AttrNumber) (*this_resno)++,
- type_id,
- (Size) type_len,
- attrname,
- (Index) 0,
- (Oid) 0,
- 0);
- te->expr = (Node *) varnode;
- if (te_head == NIL)
- te_head = te_tail = lcons(te, NIL);
- else
- te_tail = lappend(te_tail, te);
- }
-
- heap_close(rdesc);
- return (te_head);
+ 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_in_where_clause = false;
+ pstate->p_target_relation = NULL;
+ pstate->p_target_rangetblentry = NULL;
+
+ return (pstate);
}
-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 *
+Node *
make_operand(char *opname,
Node *tree,
Oid orig_typeId,
@@ -267,7 +84,7 @@ make_operand(char *opname,
if (tree != NULL)
{
result = tree;
- true_type = get_id_type(true_typeId);
+ true_type = typeidType(true_typeId);
disallow_setop(opname, true_type, result);
if (true_typeId != orig_typeId)
{ /* must coerce */
@@ -276,13 +93,13 @@ make_operand(char *opname,
Assert(nodeTag(result) == T_Const);
val = (Datum) textout((struct varlena *)
con->constvalue);
- infunc = typeid_get_retinfunc(true_typeId);
+ infunc = typeidRetinfunc(true_typeId);
con = makeNode(Const);
con->consttype = true_typeId;
- con->constlen = tlen(true_type);
+ con->constlen = typeLen(true_type);
con->constvalue = (Datum) fmgr(infunc,
val,
- get_typelem(true_typeId),
+ typeidTypElem(true_typeId),
-1 /* for varchar() type */ );
con->constisnull = false;
con->constbyval = true;
@@ -307,6 +124,21 @@ make_operand(char *opname,
}
+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, typeTypeName(optype));
+ elog(WARN, "but '%s' takes single values, not sets.",
+ op);
+ }
+}
+
Expr *
make_op(char *opname, Node *ltree, Node *rtree)
{
@@ -367,30 +199,30 @@ make_op(char *opname, Node *ltree, Node *rtree)
CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const &&
!((Const *) rtree)->constiscast)
{
- outfunc = typeid_get_retoutfunc(rtypeId);
- infunc = typeid_get_retinfunc(ltypeId);
+ outfunc = typeidRetoutfunc(rtypeId);
+ infunc = typeidRetinfunc(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);
+ newtype = typeidType(rtypeId);
+ ((Const *) rtree)->constlen = typeLen(newtype);
+ ((Const *) rtree)->constbyval = typeByVal(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);
+ outfunc = typeidRetoutfunc(ltypeId);
+ infunc = typeidRetinfunc(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);
+ newtype = typeidType(ltypeId);
+ ((Const *) ltree)->constlen = typeLen(newtype);
+ ((Const *) ltree)->constbyval = typeByVal(newtype);
}
temp = oper(opname, ltypeId, rtypeId, false);
@@ -426,38 +258,6 @@ make_op(char *opname, Node *ltree, Node *rtree)
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)
{
@@ -476,10 +276,8 @@ make_var(ParseState *pstate, char *refname, char *attrname, Oid *type_id)
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);
+ attid = attnameAttNum(rd, attrname); /* could elog(WARN) */
+ vartypeid = attnumTypeId(rd, attid);
varnode = makeVar(vnum, attid, vartypeid, vnum, attid);
@@ -667,7 +465,7 @@ make_const(Value *value)
switch (nodeTag(value))
{
case T_Integer:
- tp = type("int4");
+ tp = typeidType(INT4OID);
val = Int32GetDatum(intVal(value));
break;
@@ -675,7 +473,7 @@ make_const(Value *value)
{
float64 dummy;
- tp = type("float8");
+ tp = typeidType(FLOAT8OID);
dummy = (float64) palloc(sizeof(float64data));
*dummy = floatVal(value);
@@ -685,7 +483,7 @@ make_const(Value *value)
break;
case T_String:
- tp = type("unknown"); /* unknown for now, will be type
+ tp = typeidType(UNKNOWNOID); /* unknown for now, will be type
* coerced */
val = PointerGetDatum(textin(strVal(value)));
break;
@@ -702,111 +500,14 @@ make_const(Value *value)
}
}
- con = makeConst(typeid(tp),
- tlen(tp),
+ con = makeConst(typeTypeId(tp),
+ typeLen(tp),
val,
false,
- tbyval(tp),
+ typeByVal(tp),
false, /* not a set */
false);
return (con);
}
-/*
- * param_type_init()
- *
- * keep enough information around fill out the type of param nodes
- * used in postquel functions
- */
-void
-param_type_init(Oid *typev, int nargs)
-{
- pfunc_num_args = nargs;
- param_type_info = typev;
-}
-
-Oid
-param_type(int t)
-{
- if ((t > pfunc_num_args) || (t == 0))
- return InvalidOid;
- return param_type_info[t - 1];
-}
-
-/*
- * handleTargetColname -
- * use column names from insert
- */
-void
-handleTargetColname(ParseState *pstate, char **resname,
- char *refname, char *colname)
-{
- if (pstate->p_is_insert)
- {
- if (pstate->p_insert_columns != NIL)
- {
- Ident *id = lfirst(pstate->p_insert_columns);
-
- *resname = id->name;
- pstate->p_insert_columns = lnext(pstate->p_insert_columns);
- }
- else
- elog(WARN, "insert: more expressions than target columns");
- }
- if (pstate->p_is_insert || pstate->p_is_update)
- checkTargetTypes(pstate, *resname, refname, colname);
-}
-
-/*
- * checkTargetTypes -
- * checks value and target column types
- */
-static void
-checkTargetTypes(ParseState *pstate, char *target_colname,
- char *refname, char *colname)
-{
- Oid attrtype_id,
- attrtype_target;
- int resdomno_id,
- resdomno_target;
- Relation rd;
- RangeTblEntry *rte;
-
- if (target_colname == NULL || colname == NULL)
- return;
-
- if (refname != NULL)
- rte = refnameRangeTableEntry(pstate->p_rtable, refname);
- else
- {
- rte = colnameRangeTableEntry(pstate, colname);
- if (rte == (RangeTblEntry *) NULL)
- elog(WARN, "attribute %s not found", colname);
- refname = rte->refname;
- }
-
-/*
- if (pstate->p_is_insert && rte == pstate->p_target_rangetblentry)
- elog(WARN, "%s not available in this context", colname);
-*/
- rd = heap_open(rte->relid);
-
- resdomno_id = varattno(rd, colname);
- attrtype_id = att_typeid(rd, resdomno_id);
-
- resdomno_target = varattno(pstate->p_target_relation, target_colname);
- attrtype_target = att_typeid(pstate->p_target_relation, resdomno_target);
-
- if (attrtype_id != attrtype_target)
- elog(WARN, "Type of %s does not match target column %s",
- colname, target_colname);
-
- if ((attrtype_id == BPCHAROID || attrtype_id == VARCHAROID) &&
- rd->rd_att->attrs[resdomno_id - 1]->attlen !=
- pstate->p_target_relation->rd_att->attrs[resdomno_target - 1]->attlen)
- elog(WARN, "Length of %s does not match length of target column %s",
- colname, target_colname);
-
- heap_close(rd);
-}
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
new file mode 100644
index 0000000000..d82a46bc2f
--- /dev/null
+++ b/src/backend/parser/parse_oper.c
@@ -0,0 +1,613 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_oper.h
+ * handle operator things for parser
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.1 1997/11/25 22:05:43 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include <fmgr.h>
+
+#include <access/heapam.h>
+#include <access/relscan.h>
+#include <catalog/catname.h>
+#include <catalog/pg_operator.h>
+#include <catalog/pg_proc.h>
+#include <catalog/pg_type.h>
+#include <parser/parse_oper.h>
+#include <parser/parse_type.h>
+#include <storage/bufmgr.h>
+#include <utils/syscache.h>
+
+#ifdef 0
+#include "lib/dllist.h"
+#include "utils/datum.h"
+
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
+#include "catalog/indexing.h"
+#include "catalog/catname.h"
+
+#include "access/skey.h"
+#include "access/relscan.h"
+#include "access/tupdesc.h"
+#include "access/htup.h"
+#include "access/genam.h"
+#include "access/itup.h"
+#include "access/tupmacs.h"
+#include "storage/buf.h"
+#include "utils/lsyscache.h"
+#include "storage/lmgr.h"
+
+#include "port-protos.h" /* strdup() */
+#endif
+
+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;
+}
+
+/* given operator, return the operator OID */
+Oid
+oprid(Operator op)
+{
+ return (op->t_oid);
+}
+
+/*
+ * given opname, leftTypeId and rightTypeId,
+ * find all possible (arg1, arg2) pairs for which an operator named
+ * opname exists, such that leftTypeId can be coerced to arg1 and
+ * rightTypeId can be coerced to arg2
+ */
+int
+binary_oper_get_candidates(char *opname,
+ Oid leftTypeId,
+ Oid rightTypeId,
+ CandidateList *candidates)
+{
+ CandidateList current_candidate;
+ Relation pg_operator_desc;
+ HeapScanDesc pg_operator_scan;
+ HeapTuple tup;
+ OperatorTupleForm oper;
+ Buffer buffer;
+ int nkeys;
+ int ncandidates = 0;
+ ScanKeyData opKey[3];
+
+ *candidates = NULL;
+
+ ScanKeyEntryInitialize(&opKey[0], 0,
+ Anum_pg_operator_oprname,
+ NameEqualRegProcedure,
+ NameGetDatum(opname));
+
+ ScanKeyEntryInitialize(&opKey[1], 0,
+ Anum_pg_operator_oprkind,
+ CharacterEqualRegProcedure,
+ CharGetDatum('b'));
+
+
+ if (leftTypeId == UNKNOWNOID)
+ {
+ if (rightTypeId == UNKNOWNOID)
+ {
+ nkeys = 2;
+ }
+ else
+ {
+ nkeys = 3;
+
+ ScanKeyEntryInitialize(&opKey[2], 0,
+ Anum_pg_operator_oprright,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(rightTypeId));
+ }
+ }
+ else if (rightTypeId == UNKNOWNOID)
+ {
+ nkeys = 3;
+
+ ScanKeyEntryInitialize(&opKey[2], 0,
+ Anum_pg_operator_oprleft,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(leftTypeId));
+ }
+ else
+ /* currently only "unknown" can be coerced */
+ return 0;
+
+ pg_operator_desc = heap_openr(OperatorRelationName);
+ pg_operator_scan = heap_beginscan(pg_operator_desc,
+ 0,
+ true,
+ nkeys,
+ opKey);
+
+ do
+ {
+ tup = heap_getnext(pg_operator_scan, 0, &buffer);
+ if (HeapTupleIsValid(tup))
+ {
+ current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
+ current_candidate->args = (Oid *) palloc(2 * sizeof(Oid));
+
+ oper = (OperatorTupleForm) GETSTRUCT(tup);
+ current_candidate->args[0] = oper->oprleft;
+ current_candidate->args[1] = oper->oprright;
+ current_candidate->next = *candidates;
+ *candidates = current_candidate;
+ ncandidates++;
+ ReleaseBuffer(buffer);
+ }
+ } while (HeapTupleIsValid(tup));
+
+ heap_endscan(pg_operator_scan);
+ heap_close(pg_operator_desc);
+
+ return ncandidates;
+}
+
+/*
+ * equivalentOpersAfterPromotion -
+ * checks if a list of candidate operators obtained from
+ * binary_oper_get_candidates() contain equivalent operators. If
+ * this routine is called, we have more than 1 candidate and need to
+ * decided whether to pick one of them. This routine returns true if
+ * the all the candidates operate on the same data types after
+ * promotion (int2, int4, float4 -> float8).
+ */
+bool
+equivalentOpersAfterPromotion(CandidateList candidates)
+{
+ CandidateList result;
+ CandidateList promotedCandidates = NULL;
+ Oid leftarg,
+ rightarg;
+
+ for (result = candidates; result != NULL; result = result->next)
+ {
+ CandidateList c;
+
+ c = (CandidateList) palloc(sizeof(*c));
+ c->args = (Oid *) palloc(2 * sizeof(Oid));
+ switch (result->args[0])
+ {
+ case FLOAT4OID:
+ case INT4OID:
+ case INT2OID:
+ case CASHOID:
+ c->args[0] = FLOAT8OID;
+ break;
+ default:
+ c->args[0] = result->args[0];
+ break;
+ }
+ switch (result->args[1])
+ {
+ case FLOAT4OID:
+ case INT4OID:
+ case INT2OID:
+ case CASHOID:
+ c->args[1] = FLOAT8OID;
+ break;
+ default:
+ c->args[1] = result->args[1];
+ break;
+ }
+ c->next = promotedCandidates;
+ promotedCandidates = c;
+ }
+
+ /*
+ * if we get called, we have more than 1 candidates so we can do the
+ * following safely
+ */
+ leftarg = promotedCandidates->args[0];
+ rightarg = promotedCandidates->args[1];
+
+ for (result = promotedCandidates->next; result != NULL; result = result->next)
+ {
+ if (result->args[0] != leftarg || result->args[1] != rightarg)
+
+ /*
+ * this list contains operators that operate on different data
+ * types even after promotion. Hence we can't decide on which
+ * one to pick. The user must do explicit type casting.
+ */
+ return FALSE;
+ }
+
+ /*
+ * all the candidates are equivalent in the following sense: they
+ * operate on equivalent data types and picking any one of them is as
+ * good.
+ */
+ return TRUE;
+}
+
+
+/*
+ * given a choice of argument type pairs for a binary operator,
+ * try to choose a default pair
+ */
+CandidateList
+binary_oper_select_candidate(Oid arg1,
+ Oid arg2,
+ CandidateList candidates)
+{
+ CandidateList result;
+
+ /*
+ * if both are "unknown", there is no way to select a candidate
+ *
+ * current wisdom holds that the default operator should be one in which
+ * both operands have the same type (there will only be one such
+ * operator)
+ *
+ * 7.27.93 - I have decided not to do this; it's too hard to justify, and
+ * it's easy enough to typecast explicitly -avi [the rest of this
+ * routine were commented out since then -ay]
+ */
+
+ if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID)
+ return (NULL);
+
+ /*
+ * 6/23/95 - I don't complete agree with avi. In particular, casting
+ * floats is a pain for users. Whatever the rationale behind not doing
+ * this is, I need the following special case to work.
+ *
+ * In the WHERE clause of a query, if a float is specified without
+ * quotes, we treat it as float8. I added the float48* operators so
+ * that we can operate on float4 and float8. But now we have more than
+ * one matching operator if the right arg is unknown (eg. float
+ * specified with quotes). This break some stuff in the regression
+ * test where there are floats in quotes not properly casted. Below is
+ * the solution. In addition to requiring the operator operates on the
+ * same type for both operands [as in the code Avi originally
+ * commented out], we also require that the operators be equivalent in
+ * some sense. (see equivalentOpersAfterPromotion for details.) - ay
+ * 6/95
+ */
+ if (!equivalentOpersAfterPromotion(candidates))
+ return NULL;
+
+ /*
+ * if we get here, any one will do but we're more picky and require
+ * both operands be the same.
+ */
+ for (result = candidates; result != NULL; result = result->next)
+ {
+ if (result->args[0] == result->args[1])
+ return result;
+ }
+
+ return (NULL);
+}
+
+/* Given operator, types of arg1, and arg2, return oper struct */
+/* arg1, arg2 --typeids */
+Operator
+oper(char *op, Oid arg1, Oid arg2, bool noWarnings)
+{
+ HeapTuple tup;
+ CandidateList candidates;
+ int ncandidates;
+
+ if (!arg2)
+ arg2 = arg1;
+ if (!arg1)
+ arg1 = arg2;
+
+ if (!(tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(arg1),
+ ObjectIdGetDatum(arg2),
+ Int8GetDatum('b'))))
+ {
+ ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);
+ if (ncandidates == 0)
+ {
+
+ /*
+ * no operators of the desired types found
+ */
+ if (!noWarnings)
+ op_error(op, arg1, arg2);
+ return (NULL);
+ }
+ else if (ncandidates == 1)
+ {
+
+ /*
+ * exactly one operator of the desired types found
+ */
+ tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(candidates->args[0]),
+ ObjectIdGetDatum(candidates->args[1]),
+ Int8GetDatum('b'));
+ Assert(HeapTupleIsValid(tup));
+ }
+ else
+ {
+
+ /*
+ * multiple operators of the desired types found
+ */
+ candidates = binary_oper_select_candidate(arg1, arg2, candidates);
+ if (candidates != NULL)
+ {
+ /* we chose one of them */
+ tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(candidates->args[0]),
+ ObjectIdGetDatum(candidates->args[1]),
+ Int8GetDatum('b'));
+ Assert(HeapTupleIsValid(tup));
+ }
+ else
+ {
+ Type tp1,
+ tp2;
+
+ /* we chose none of them */
+ tp1 = typeidType(arg1);
+ tp2 = typeidType(arg2);
+ if (!noWarnings)
+ {
+ elog(NOTICE, "there is more than one operator %s for types", op);
+ elog(NOTICE, "%s and %s. You will have to retype this query",
+ typeTypeName(tp1), typeTypeName(tp2));
+ elog(WARN, "using an explicit cast");
+ }
+ return (NULL);
+ }
+ }
+ }
+ return ((Operator) tup);
+}
+
+/*
+ * given opname and typeId, find all possible types for which
+ * a right/left unary operator named opname exists,
+ * such that typeId can be coerced to it
+ */
+int
+unary_oper_get_candidates(char *op,
+ Oid typeId,
+ CandidateList *candidates,
+ char rightleft)
+{
+ CandidateList current_candidate;
+ Relation pg_operator_desc;
+ HeapScanDesc pg_operator_scan;
+ HeapTuple tup;
+ OperatorTupleForm oper;
+ Buffer buffer;
+ int ncandidates = 0;
+
+ static ScanKeyData opKey[2] = {
+ {0, Anum_pg_operator_oprname, NameEqualRegProcedure},
+ {0, Anum_pg_operator_oprkind, CharacterEqualRegProcedure}};
+
+ *candidates = NULL;
+
+ fmgr_info(NameEqualRegProcedure, (func_ptr *) &opKey[0].sk_func,
+ &opKey[0].sk_nargs);
+ opKey[0].sk_argument = NameGetDatum(op);
+ fmgr_info(CharacterEqualRegProcedure, (func_ptr *) &opKey[1].sk_func,
+ &opKey[1].sk_nargs);
+ opKey[1].sk_argument = CharGetDatum(rightleft);
+
+ /* currently, only "unknown" can be coerced */
+
+ /*
+ * but we should allow types that are internally the same to be
+ * "coerced"
+ */
+ if (typeId != UNKNOWNOID)
+ {
+ return 0;
+ }
+
+ pg_operator_desc = heap_openr(OperatorRelationName);
+ pg_operator_scan = heap_beginscan(pg_operator_desc,
+ 0,
+ true,
+ 2,
+ opKey);
+
+ do
+ {
+ tup = heap_getnext(pg_operator_scan, 0, &buffer);
+ if (HeapTupleIsValid(tup))
+ {
+ current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
+ current_candidate->args = (Oid *) palloc(sizeof(Oid));
+
+ oper = (OperatorTupleForm) GETSTRUCT(tup);
+ if (rightleft == 'r')
+ current_candidate->args[0] = oper->oprleft;
+ else
+ current_candidate->args[0] = oper->oprright;
+ current_candidate->next = *candidates;
+ *candidates = current_candidate;
+ ncandidates++;
+ ReleaseBuffer(buffer);
+ }
+ } while (HeapTupleIsValid(tup));
+
+ heap_endscan(pg_operator_scan);
+ heap_close(pg_operator_desc);
+
+ return ncandidates;
+}
+
+/* Given unary right-side operator (operator on right), return oper struct */
+/* arg-- type id */
+Operator
+right_oper(char *op, Oid arg)
+{
+ HeapTuple tup;
+ CandidateList candidates;
+ int ncandidates;
+
+ /*
+ * if (!OpCache) { init_op_cache(); }
+ */
+ if (!(tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(arg),
+ ObjectIdGetDatum(InvalidOid),
+ Int8GetDatum('r'))))
+ {
+ ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r');
+ if (ncandidates == 0)
+ {
+ elog(WARN,
+ "Can't find right op: %s for type %d", op, arg);
+ return (NULL);
+ }
+ else if (ncandidates == 1)
+ {
+ tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(candidates->args[0]),
+ ObjectIdGetDatum(InvalidOid),
+ Int8GetDatum('r'));
+ Assert(HeapTupleIsValid(tup));
+ }
+ else
+ {
+ elog(NOTICE, "there is more than one right operator %s", op);
+ elog(NOTICE, "you will have to retype this query");
+ elog(WARN, "using an explicit cast");
+ return (NULL);
+ }
+ }
+ return ((Operator) tup);
+}
+
+/* Given unary left-side operator (operator on left), return oper struct */
+/* arg--type id */
+Operator
+left_oper(char *op, Oid arg)
+{
+ HeapTuple tup;
+ CandidateList candidates;
+ int ncandidates;
+
+ /*
+ * if (!OpCache) { init_op_cache(); }
+ */
+ if (!(tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(InvalidOid),
+ ObjectIdGetDatum(arg),
+ Int8GetDatum('l'))))
+ {
+ ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l');
+ if (ncandidates == 0)
+ {
+ elog(WARN,
+ "Can't find left op: %s for type %d", op, arg);
+ return (NULL);
+ }
+ else if (ncandidates == 1)
+ {
+ tup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(op),
+ ObjectIdGetDatum(InvalidOid),
+ ObjectIdGetDatum(candidates->args[0]),
+ Int8GetDatum('l'));
+ Assert(HeapTupleIsValid(tup));
+ }
+ else
+ {
+ elog(NOTICE, "there is more than one left operator %s", op);
+ elog(NOTICE, "you will have to retype this query");
+ elog(WARN, "using an explicit cast");
+ return (NULL);
+ }
+ }
+ return ((Operator) tup);
+}
+
+/* Given a typename and value, returns the ascii form of the value */
+
+#ifdef NOT_USED
+char *
+outstr(char *typename, /* Name of type of value */
+ char *value) /* Could be of any type */
+{
+ TypeTupleForm tp;
+ Oid op;
+
+ tp = (TypeTupleForm) GETSTRUCT(type(typename));
+ op = tp->typoutput;
+ return ((char *) fmgr(op, value));
+}
+
+#endif
+
+/*
+ * Give a somewhat useful error message when the operator for two types
+ * is not found.
+ */
+void
+op_error(char *op, Oid arg1, Oid arg2)
+{
+ Type tp1 = NULL,
+ tp2 = NULL;
+
+ if (typeidIsValid(arg1))
+ {
+ tp1 = typeidType(arg1);
+ }
+ else
+ {
+ elog(WARN, "left hand side of operator %s has an unknown type, probably a bad attribute name", op);
+ }
+
+ if (typeidIsValid(arg2))
+ {
+ tp2 = typeidType(arg2);
+ }
+ else
+ {
+ elog(WARN, "right hand side of operator %s has an unknown type, probably a bad attribute name", op);
+ }
+
+ elog(NOTICE, "there is no operator %s for types %s and %s",
+ op, typeTypeName(tp1), typeTypeName(tp2));
+ elog(NOTICE, "You will either have to retype this query using an");
+ elog(NOTICE, "explicit cast, or you will have to define the operator");
+ elog(WARN, "%s for %s and %s using CREATE OPERATOR",
+ op, typeTypeName(tp1), typeTypeName(tp2));
+}
+
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
new file mode 100644
index 0000000000..dd3fa2787a
--- /dev/null
+++ b/src/backend/parser/parse_relation.c
@@ -0,0 +1,480 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_relation.c--
+ * parser support routines dealing with relations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.1 1997/11/25 22:05:45 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+
+#include "postgres.h"
+#include "access/heapam.h"
+#include <access/htup.h>
+#include <catalog/pg_type.h>
+#include "nodes/makefuncs.h"
+#include <parser/parse_relation.h>
+#include <utils/acl.h>
+#include "utils/builtins.h"
+#include <utils/lsyscache.h>
+
+#ifdef 0
+#include "fmgr.h"
+#include "access/tupmacs.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/acl.h" /* for ACL_NO_PRIV_WARNING */
+
+#include "utils/syscache.h"
+#include "catalog/pg_operator.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+#endif
+
+struct
+{
+ char *field;
+ int code;
+} special_attr[] =
+
+{
+ {
+ "ctid", SelfItemPointerAttributeNumber
+ },
+ {
+ "oid", ObjectIdAttributeNumber
+ },
+ {
+ "xmin", MinTransactionIdAttributeNumber
+ },
+ {
+ "cmin", MinCommandIdAttributeNumber
+ },
+ {
+ "xmax", MaxTransactionIdAttributeNumber
+ },
+ {
+ "cmax", MaxCommandIdAttributeNumber
+ },
+};
+
+#define SPECIALS (sizeof(special_attr)/sizeof(*special_attr))
+
+static char *attnum_type[SPECIALS] = {
+ "tid",
+ "oid",
+ "xid",
+ "cid",
+ "xid",
+ "cid",
+};
+
+/* given refname, return a pointer to the range table entry */
+RangeTblEntry *
+refnameRangeTableEntry(List *rtable, char *refname)
+{
+ List *temp;
+
+ foreach(temp, rtable)
+ {
+ RangeTblEntry *rte = lfirst(temp);
+
+ if (!strcmp(rte->refname, refname))
+ return rte;
+ }
+ return NULL;
+}
+
+/* given refname, return id of variable; position starts with 1 */
+int
+refnameRangeTablePosn(List *rtable, char *refname)
+{
+ int index;
+ List *temp;
+
+ index = 1;
+ foreach(temp, rtable)
+ {
+ RangeTblEntry *rte = lfirst(temp);
+
+ if (!strcmp(rte->refname, refname))
+ return index;
+ index++;
+ }
+ return (0);
+}
+
+/*
+ * returns range entry if found, else NULL
+ */
+RangeTblEntry *
+colnameRangeTableEntry(ParseState *pstate, char *colname)
+{
+ List *et;
+ List *rtable;
+ RangeTblEntry *rte_result;
+
+ if (pstate->p_is_rule)
+ rtable = lnext(lnext(pstate->p_rtable));
+ else
+ rtable = pstate->p_rtable;
+
+ rte_result = NULL;
+ foreach(et, rtable)
+ {
+ RangeTblEntry *rte = lfirst(et);
+
+ /* only entries on outer(non-function?) scope */
+ if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
+ continue;
+
+ if (get_attnum(rte->relid, colname) != InvalidAttrNumber)
+ {
+ if (rte_result != NULL)
+ {
+ if (!pstate->p_is_insert ||
+ rte != pstate->p_target_rangetblentry)
+ elog(WARN, "Column %s is ambiguous", colname);
+ }
+ else
+ rte_result = rte;
+ }
+ }
+ return rte_result;
+}
+
+/*
+ * put new entry in pstate p_rtable structure, or return pointer
+ * if pstate null
+*/
+RangeTblEntry *
+addRangeTableEntry(ParseState *pstate,
+ char *relname,
+ char *refname,
+ bool inh,
+ bool inFromCl)
+{
+ Relation relation;
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+
+ if (pstate != NULL &&
+ refnameRangeTableEntry(pstate->p_rtable, refname) != NULL)
+ elog(WARN, "Table name %s specified more than once", refname);
+
+ rte->relname = pstrdup(relname);
+ rte->refname = pstrdup(refname);
+
+ relation = heap_openr(relname);
+ if (relation == NULL)
+ {
+ elog(WARN, "%s: %s",
+ relname, aclcheck_error_strings[ACLCHECK_NO_CLASS]);
+ }
+
+ /*
+ * Flags - zero or more from inheritance,union,version or
+ * recursive (transitive closure) [we don't support them all -- ay
+ * 9/94 ]
+ */
+ rte->inh = inh;
+
+ /* RelOID */
+ rte->relid = RelationGetRelationId(relation);
+
+ rte->inFromCl = inFromCl;
+
+ /*
+ * close the relation we're done with it for now.
+ */
+ if (pstate != NULL)
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ heap_close(relation);
+
+ return rte;
+}
+
+/*
+ * expandAll -
+ * makes a list of attributes
+ * assumes reldesc caching works
+ */
+List *
+expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
+{
+ Relation rdesc;
+ List *te_tail = NIL,
+ *te_head = NIL;
+ Var *varnode;
+ int varattno,
+ maxattrs;
+ Oid type_id;
+ int type_len;
+ RangeTblEntry *rte;
+
+ rte = refnameRangeTableEntry(pstate->p_rtable, refname);
+ if (rte == NULL)
+ rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE);
+
+ rdesc = heap_open(rte->relid);
+
+ if (rdesc == NULL)
+ {
+ elog(WARN, "Unable to expand all -- heap_open failed on %s",
+ rte->refname);
+ return NIL;
+ }
+ maxattrs = RelationGetNumberOfAttributes(rdesc);
+
+ for (varattno = 0; varattno <= maxattrs - 1; varattno++)
+ {
+ char *attrname;
+ char *resname = NULL;
+ TargetEntry *te = makeNode(TargetEntry);
+
+ attrname = pstrdup((rdesc->rd_att->attrs[varattno]->attname).data);
+ varnode = (Var *) make_var(pstate, refname, attrname, &type_id);
+ type_len = (int) typeLen(typeidType(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);
+}
+
+/* given relation and att name, return id of variable */
+int
+attnameAttNum(Relation rd, char *a)
+{
+ int i;
+
+ for (i = 0; i < rd->rd_rel->relnatts; i++)
+ if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a))
+ return (i + 1);
+
+ for (i = 0; i < SPECIALS; i++)
+ if (!strcmp(special_attr[i].field, a))
+ return (special_attr[i].code);
+
+ /* on failure */
+ elog(WARN, "Relation %s does not have attribute %s",
+ RelationGetRelationName(rd), a);
+ return 0; /* lint */
+}
+
+/* Given range variable, return whether attribute of this name
+ * is a set.
+ * NOTE the ASSUMPTION here that no system attributes are, or ever
+ * will be, sets.
+ */
+bool
+attnameIsSet(Relation rd, char *name)
+{
+ int i;
+
+ /* First check if this is a system attribute */
+ for (i = 0; i < SPECIALS; i++)
+ {
+ if (!strcmp(special_attr[i].field, name))
+ {
+ return (false); /* no sys attr is a set */
+ }
+ }
+ return (get_attisset(rd->rd_id, name));
+}
+
+/*-------------
+ * given an attribute number and a relation, return its relation name
+ */
+char *
+attnumAttName(Relation rd, int attrno)
+{
+ char *name;
+ int i;
+
+ if (attrno < 0)
+ {
+ for (i = 0; i < SPECIALS; i++)
+ {
+ if (special_attr[i].code == attrno)
+ {
+ name = special_attr[i].field;
+ return (name);
+ }
+ }
+ elog(WARN, "Illegal attr no %d for relation %s",
+ attrno, RelationGetRelationName(rd));
+ }
+ else if (attrno >= 1 && attrno <= RelationGetNumberOfAttributes(rd))
+ {
+ name = (rd->rd_att->attrs[attrno - 1]->attname).data;
+ return (name);
+ }
+ else
+ {
+ elog(WARN, "Illegal attr no %d for relation %s",
+ attrno, RelationGetRelationName(rd));
+ }
+
+ /*
+ * Shouldn't get here, but we want lint to be happy...
+ */
+
+ return (NULL);
+}
+
+int
+attnumAttNelems(Relation rd, int attid)
+{
+ return (rd->rd_att->attrs[attid - 1]->attnelems);
+}
+
+Oid
+attnameTypeId(Oid relid, char *attrname)
+{
+ int attid;
+ Oid vartype;
+ Relation rd;
+
+ rd = heap_open(relid);
+ if (!RelationIsValid(rd))
+ {
+ rd = heap_openr(typeidTypeName(relid));
+ if (!RelationIsValid(rd))
+ elog(WARN, "cannot compute type of att %s for relid %d",
+ attrname, relid);
+ }
+
+ attid = attnameAttNum(rd, attrname); /* could elog(WARN) and never return */
+
+ vartype = attnumTypeId(rd, attid);
+
+ /*
+ * close relation we're done with it now
+ */
+ heap_close(rd);
+
+ return (vartype);
+}
+
+/* given attribute id, return type of that attribute */
+/* XXX Special case for pseudo-attributes is a hack */
+Oid
+attnumTypeId(Relation rd, int attid)
+{
+
+ if (attid < 0)
+ return (typeTypeId(typenameType(attnum_type[-attid - 1])));
+
+ /*
+ * -1 because varattno (where attid comes from) returns one more than
+ * index
+ */
+ return (rd->rd_att->attrs[attid - 1]->atttypid);
+}
+
+/*
+ * 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 = attnameAttNum(rd, colname);
+ attrtype_id = attnumTypeId(rd, resdomno_id);
+
+ resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname);
+ attrtype_target = attnumTypeId(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);
+}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
new file mode 100644
index 0000000000..f29aa49d34
--- /dev/null
+++ b/src/backend/parser/parse_target.c
@@ -0,0 +1,679 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_target.c
+ * handle target lists
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.1 1997/11/25 22:05:47 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "postgres.h"
+#include "catalog/pg_type.h"
+#include "nodes/makefuncs.h"
+#include "nodes/primnodes.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_relation.h"
+#include "parser/parse_target.h"
+#include "parser/parse_node.h"
+#include "utils/builtins.h"
+
+#ifdef 0
+#include "nodes/nodes.h"
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
+#include "nodes/relation.h"
+#include "parse.h" /* for AND, OR, etc. */
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_proc.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/mcxt.h"
+#include "utils/syscache.h"
+#include "utils/acl.h"
+#include "nodes/nodeFuncs.h"
+#include "commands/sequence.h"
+
+#include "optimizer/clauses.h"
+#include "access/heapam.h"
+
+#include "miscadmin.h"
+
+#include "port-protos.h" /* strdup() */
+#endif
+
+/*
+ * transformTargetList -
+ * turns a list of ResTarget's into a list of TargetEntry's
+ */
+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 = typeLen(typeidType(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 = attnameAttNum(rd, res->name);
+ ndims = attnumAttNelems(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 = typeLen(typeidType(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);
+ }
+
+ return p_target;
+}
+
+
+/*
+ * make_targetlist_expr -
+ * make a TargetEntry from an expression
+ *
+ * arrayRef is a list of transformed A_Indices
+ */
+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 = typeLen(typeidType(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 = attnameAttNum(rd, colname);
+ attrisset = attnameIsSet(rd, colname);
+ attrtype = attnumTypeId(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 = typeLen(typeidType(attrtype));
+ }
+#if 0
+ if (Input_is_string && Typecast_ok)
+ {
+ Datum val;
+
+ if (type_id == typeTypeId(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(typeidRetinfunc(attrtype),
+ val, typeidTypElem(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, typeidType(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
+
+ 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 = typeidTypElem(attrtype);
+
+ expr = (Node *) parser_typecast2(expr,
+ type_id,
+ typeidType(typelem),
+ attrlen);
+ }
+ else
+ expr = (Node *) parser_typecast2(expr,
+ type_id,
+ typeidType(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,
+ typeidTypeName(attrtype),
+ typeidTypeName(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 = attnumTypeId(rd, resdomno);
+ attrlen = typeLen(typeidType(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;
+}
+
+/*
+ * 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)
+ */
+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);
+ 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)
+ {
+ List *nxt;
+ char *name = ((Ident *) lfirst(tl))->name;
+
+ /* elog on failure */
+ attnameAttNum(pstate->p_target_relation, name);
+ foreach(nxt, lnext(tl))
+ if (!strcmp(name, ((Ident *) lfirst(nxt))->name))
+ elog (WARN, "Attribute '%s' should be specified only once", name);
+ }
+ }
+
+ return cols;
+}
+
+/*
+ * expandAllTables -
+ * turns '*' (in the target list) into a list of attributes
+ * (of all relations in the range table)
+ */
+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.
+ *
+ */
+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?";
+}
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
new file mode 100644
index 0000000000..67632cdab6
--- /dev/null
+++ b/src/backend/parser/parse_type.c
@@ -0,0 +1,319 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_type.h
+ * handle type operations for parser
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.1 1997/11/25 22:05:51 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include "fmgr.h"
+
+#include <catalog/pg_type.h>
+#include <parser/parse_target.h>
+#include <parser/parse_type.h>
+#include "utils/syscache.h"
+
+#ifdef 0
+#include "lib/dllist.h"
+#include "utils/datum.h"
+
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "catalog/catname.h"
+
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
+#include "catalog/indexing.h"
+#include "catalog/catname.h"
+
+#include "access/skey.h"
+#include "access/relscan.h"
+#include "access/tupdesc.h"
+#include "access/htup.h"
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/itup.h"
+#include "access/tupmacs.h"
+
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/lsyscache.h"
+#include "storage/lmgr.h"
+
+#include "port-protos.h" /* strdup() */
+#endif
+
+/* check to see if a type id is valid,
+ * returns true if it is. By using this call before calling
+ * typeidType or typeidTypeName, more meaningful error messages
+ * can be produced because the caller typically has more context of
+ * what's going on - jolly
+ */
+bool
+typeidIsValid(Oid id)
+{
+ return (SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(id),
+ 0, 0, 0) != NULL);
+}
+
+/* return a type name, given a typeid */
+char *
+typeidTypeName(Oid id)
+{
+ HeapTuple tup;
+ TypeTupleForm typetuple;
+
+ if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
+ 0, 0, 0)))
+ {
+ elog(WARN, "type id lookup of %ud failed", id);
+ return (NULL);
+ }
+ typetuple = (TypeTupleForm) GETSTRUCT(tup);
+ return (typetuple->typname).data;
+}
+
+/* return a Type structure, given an typid */
+Type
+typeidType(Oid id)
+{
+ HeapTuple tup;
+
+ if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
+ 0, 0, 0)))
+ {
+ elog(WARN, "type id lookup of %ud failed", id);
+ return (NULL);
+ }
+ return ((Type) tup);
+}
+
+/* return a Type structure, given type name */
+Type
+typenameType(char *s)
+{
+ HeapTuple tup;
+
+ if (s == NULL)
+ {
+ elog(WARN, "type(): Null type");
+ }
+
+ if (!(tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(s), 0, 0, 0)))
+ {
+ elog(WARN, "type name lookup of %s failed", s);
+ }
+ return ((Type) tup);
+}
+
+/* given type, return the type OID */
+Oid
+typeTypeId(Type tp)
+{
+ if (tp == NULL)
+ elog(WARN, "typeTypeId() called with NULL type struct");
+ return (tp->t_oid);
+}
+
+/* given type (as type struct), return the length of type */
+int16
+typeLen(Type t)
+{
+ TypeTupleForm typ;
+
+ typ = (TypeTupleForm) GETSTRUCT(t);
+ return (typ->typlen);
+}
+
+/* given type (as type struct), return the value of its 'byval' attribute.*/
+bool
+typeByVal(Type t)
+{
+ TypeTupleForm typ;
+
+ typ = (TypeTupleForm) GETSTRUCT(t);
+ return (typ->typbyval);
+}
+
+/* given type (as type struct), return the name of type */
+char *
+typeTypeName(Type t)
+{
+ TypeTupleForm typ;
+
+ typ = (TypeTupleForm) GETSTRUCT(t);
+ return (typ->typname).data;
+}
+
+/* given a type, return its typetype ('c' for 'c'atalog types) */
+char
+typeTypeFlag(Type t)
+{
+ TypeTupleForm typ;
+
+ typ = (TypeTupleForm) GETSTRUCT(t);
+ return (typ->typtype);
+}
+
+/* Given a type structure and a string, returns the internal form of
+ that string */
+char *
+stringTypeString(Type tp, char *string, int typlen)
+{
+ Oid op;
+ Oid typelem;
+
+ op = ((TypeTupleForm) GETSTRUCT(tp))->typinput;
+ typelem = ((TypeTupleForm) GETSTRUCT(tp))->typelem; /* XXX - used for array_in */
+ /* typlen is for bpcharin() and varcharin() */
+ return ((char *) fmgr(op, string, typelem, typlen));
+}
+
+/* Given a type id, returns the out-conversion function of the type */
+Oid
+typeidRetoutfunc(Oid type_id)
+{
+ HeapTuple typeTuple;
+ TypeTupleForm type;
+ Oid outfunc;
+
+ typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type_id),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(typeTuple))
+ elog(WARN, "typeidRetoutfunc: Invalid type - oid = %u", type_id);
+
+ type = (TypeTupleForm) GETSTRUCT(typeTuple);
+ outfunc = type->typoutput;
+ return (outfunc);
+}
+
+Oid
+typeidTypeRelid(Oid type_id)
+{
+ HeapTuple typeTuple;
+ TypeTupleForm type;
+ Oid infunc;
+
+ typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type_id),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(typeTuple))
+ elog(WARN, "typeidTypeRelid: Invalid type - oid = %u", type_id);
+
+ type = (TypeTupleForm) GETSTRUCT(typeTuple);
+ infunc = type->typrelid;
+ return (infunc);
+}
+
+Oid
+typeTypeRelid(Type typ)
+{
+ TypeTupleForm typtup;
+
+ typtup = (TypeTupleForm) GETSTRUCT(typ);
+
+ return (typtup->typrelid);
+}
+
+Oid
+typeidTypElem(Oid type_id)
+{
+ HeapTuple typeTuple;
+ TypeTupleForm type;
+
+ if (!(typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type_id),
+ 0, 0, 0)))
+ {
+ elog(WARN, "type id lookup of %u failed", type_id);
+ }
+ type = (TypeTupleForm) GETSTRUCT(typeTuple);
+
+ return (type->typelem);
+}
+
+/* Given the attribute type of an array return the arrtribute type of
+ an element of the array */
+
+Oid
+GetArrayElementType(Oid typearray)
+{
+ HeapTuple type_tuple;
+ TypeTupleForm type_struct_array;
+
+ type_tuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(typearray),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(type_tuple))
+ elog(WARN, "GetArrayElementType: Cache lookup failed for type %d",
+ 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, "GetArrayElementType: type %s is not an array",
+ (Name) &(type_struct_array->typname.data[0]));
+ }
+
+ return (type_struct_array->typelem);
+}
+
+/* Given a type id, returns the in-conversion function of the type */
+Oid
+typeidRetinfunc(Oid type_id)
+{
+ HeapTuple typeTuple;
+ TypeTupleForm type;
+ Oid infunc;
+
+ typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type_id),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(typeTuple))
+ elog(WARN, "typeidRetinfunc: Invalid type - oid = %u", type_id);
+
+ type = (TypeTupleForm) GETSTRUCT(typeTuple);
+ infunc = type->typinput;
+ return (infunc);
+}
+
+
+#ifdef NOT_USED
+char
+FindDelimiter(char *typename)
+{
+ char delim;
+ HeapTuple typeTuple;
+ TypeTupleForm type;
+
+
+ if (!(typeTuple = SearchSysCacheTuple(TYPNAME,
+ PointerGetDatum(typename),
+ 0, 0, 0)))
+ {
+ elog(WARN, "type name lookup of %s failed", typename);
+ }
+ type = (TypeTupleForm) GETSTRUCT(typeTuple);
+
+ delim = type->typdelim;
+ return (delim);
+}
+
+#endif
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index d658c30896..ea309c3c28 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.28 1997/11/20 23:22:24 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.29 1997/11/25 22:05:52 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -14,9 +14,20 @@
#include <stdio.h>
#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "parser/parser.h"
+#include "parser/analyze.h"
+#include "parser/parse_node.h"
+
+void init_io(); /* from scan.l */
+void parser_init(Oid *typev, int nargs); /* from gram.y */
+int yyparse(); /* from gram.c */
+
+#ifdef 0
+#include "parser/parse.h"
#include "parser/gramparse.h"
-#include "parser/parse_query.h"
#include "utils/palloc.h"
+#endif
char *parseString; /* the char* which holds the string to be
* parsed */
@@ -103,10 +114,10 @@ static void
define_sets(Node *clause)
{
Oid setoid;
- Type t = type("oid");
- Oid typeoid = typeid(t);
- Size oidsize = tlen(t);
- bool oidbyval = tbyval(t);
+ Type t = typeidType(OIDOID);
+ Oid typeoid = typeTypeId(t);
+ Size oidsize = typeLen(t);
+ bool oidbyval = typeByVal(t);
if (clause == NULL)
{
@@ -125,11 +136,11 @@ define_sets(Node *clause)
return;
}
setoid = SetDefine(((Const *) clause)->constvalue,
- get_id_typname(((Const *) clause)->consttype));
+ typeidTypeName(((Const *) clause)->consttype));
set_constvalue((Const) clause, setoid);
set_consttype((Const) clause, typeoid);
set_constlen((Const) clause, oidsize);
- set_constbyval((Const) clause, oidbyval);
+ set_constypeByVal((Const) clause, oidbyval);
}
else if (IsA(clause, Iter))
{
@@ -173,6 +184,5 @@ define_sets(Node *clause)
define_sets(get_rightop(clause));
}
}
-
#endif
diff --git a/src/backend/parser/scansup.c b/src/backend/parser/scansup.c
index dcc66145a5..e50757940a 100644
--- a/src/backend/parser/scansup.c
+++ b/src/backend/parser/scansup.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/scansup.c,v 1.7 1997/09/08 02:25:22 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/scansup.c,v 1.8 1997/11/25 22:05:55 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,7 +17,6 @@
#include <ctype.h>
#include <string.h>
-#include "c.h"
#include "postgres.h"
#include "miscadmin.h"
#include "utils/elog.h"