aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_target.c
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/parse_target.c
parent3aff4011c735faa747ce94d20da6fd9f85144955 (diff)
downloadpostgresql-4a5b781d71b61887fd312112d75979f250bf723f.tar.gz
postgresql-4a5b781d71b61887fd312112d75979f250bf723f.zip
Break parser functions into smaller files, group together.
Diffstat (limited to 'src/backend/parser/parse_target.c')
-rw-r--r--src/backend/parser/parse_target.c679
1 files changed, 679 insertions, 0 deletions
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
new file mode 100644
index 00000000000..f29aa49d342
--- /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?";
+}