summaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_target.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_target.c')
-rw-r--r--src/backend/parser/parse_target.c561
1 files changed, 0 insertions, 561 deletions
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
deleted file mode 100644
index 608ca7613dc..00000000000
--- a/src/backend/parser/parse_target.c
+++ /dev/null
@@ -1,561 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * parse_target.c
- * handle target lists
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.85 2002/06/20 20:29:33 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "miscadmin.h"
-#include "nodes/makefuncs.h"
-#include "parser/parsetree.h"
-#include "parser/parse_coerce.h"
-#include "parser/parse_expr.h"
-#include "parser/parse_func.h"
-#include "parser/parse_relation.h"
-#include "parser/parse_target.h"
-#include "parser/parse_type.h"
-#include "utils/builtins.h"
-
-
-static List *ExpandAllTables(ParseState *pstate);
-static char *FigureColname(Node *node);
-static int FigureColnameInternal(Node *node, char **name);
-
-
-/*
- * transformTargetEntry()
- * Transform any ordinary "expression-type" node into a targetlist entry.
- * This is exported so that parse_clause.c can generate targetlist entries
- * for ORDER/GROUP BY items that are not already in the targetlist.
- *
- * node the (untransformed) parse tree for the value expression.
- * expr the transformed expression, or NULL if caller didn't do it yet.
- * colname the column name to be assigned, or NULL if none yet set.
- * resjunk true if the target should be marked resjunk, ie, it is not
- * wanted in the final projected tuple.
- */
-TargetEntry *
-transformTargetEntry(ParseState *pstate,
- Node *node,
- Node *expr,
- char *colname,
- bool resjunk)
-{
- Oid type_id;
- int32 type_mod;
- Resdom *resnode;
-
- /* Transform the node if caller didn't do it already */
- if (expr == NULL)
- expr = transformExpr(pstate, node);
-
- if (IsA(expr, RangeVar))
- elog(ERROR, "You can't use relation names alone in the target list, try relation.*.");
-
- type_id = exprType(expr);
- type_mod = exprTypmod(expr);
-
- if (colname == NULL)
- {
- /*
- * Generate a suitable column name for a column without any
- * explicit 'AS ColumnName' clause.
- */
- colname = FigureColname(node);
- }
-
- resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
- type_id,
- type_mod,
- colname,
- resjunk);
-
- return makeTargetEntry(resnode, expr);
-}
-
-
-/*
- * transformTargetList()
- * Turns a list of ResTarget's into a list of TargetEntry's.
- *
- * At this point, we don't care whether we are doing SELECT, INSERT,
- * or UPDATE; we just transform the given expressions.
- */
-List *
-transformTargetList(ParseState *pstate, List *targetlist)
-{
- List *p_target = NIL;
-
- while (targetlist != NIL)
- {
- ResTarget *res = (ResTarget *) lfirst(targetlist);
-
- if (IsA(res->val, ColumnRef))
- {
- ColumnRef *cref = (ColumnRef *) res->val;
- List *fields = cref->fields;
- int numnames = length(fields);
-
- if (numnames == 1 && strcmp(strVal(lfirst(fields)), "*") == 0)
- {
- /*
- * Target item is a single '*', expand all tables (eg.
- * SELECT * FROM emp)
- */
- p_target = nconc(p_target,
- ExpandAllTables(pstate));
- }
- else if (strcmp(strVal(nth(numnames-1, fields)), "*") == 0)
- {
- /*
- * Target item is relation.*, expand that table (eg.
- * SELECT emp.*, dname FROM emp, dept)
- */
- char *schemaname;
- char *relname;
- RangeTblEntry *rte;
- int sublevels_up;
-
- switch (numnames)
- {
- case 2:
- schemaname = NULL;
- relname = strVal(lfirst(fields));
- break;
- case 3:
- schemaname = strVal(lfirst(fields));
- relname = strVal(lsecond(fields));
- break;
- case 4:
- {
- char *name1 = strVal(lfirst(fields));
-
- /*
- * We check the catalog name and then ignore it.
- */
- if (strcmp(name1, DatabaseName) != 0)
- elog(ERROR, "Cross-database references are not implemented");
- schemaname = strVal(lsecond(fields));
- relname = strVal(lfirst(lnext(lnext(fields))));
- break;
- }
- default:
- elog(ERROR, "Invalid qualified name syntax (too many names)");
- schemaname = NULL; /* keep compiler quiet */
- relname = NULL;
- break;
- }
-
- /* XXX do something with schema name */
- rte = refnameRangeTblEntry(pstate, relname,
- &sublevels_up);
- if (rte == NULL)
- rte = addImplicitRTE(pstate, makeRangeVar(NULL, relname));
-
- p_target = nconc(p_target,
- expandRelAttrs(pstate, rte));
- }
- else
- {
- /* Plain ColumnRef node, treat it as an expression */
- p_target = lappend(p_target,
- transformTargetEntry(pstate,
- res->val,
- NULL,
- res->name,
- false));
- }
- }
- else if (IsA(res->val, InsertDefault))
- {
- InsertDefault *newnode = makeNode(InsertDefault);
-
- /*
- * If this is a DEFAULT element, we make a junk entry
- * which will get dropped on return to transformInsertStmt().
- */
- p_target = lappend(p_target, newnode);
- }
- else
- {
- /* Everything else but ColumnRef and InsertDefault */
- p_target = lappend(p_target,
- transformTargetEntry(pstate,
- res->val,
- NULL,
- res->name,
- false));
- }
-
- targetlist = lnext(targetlist);
- }
-
- return p_target;
-}
-
-
-/*
- * updateTargetListEntry()
- * This is used in INSERT and UPDATE statements only. It prepares a
- * TargetEntry for assignment to a column of the target table.
- * This includes coercing the given value to the target column's type
- * (if necessary), and dealing with any subscripts attached to the target
- * column itself.
- *
- * pstate parse state
- * tle target list entry to be modified
- * colname target column name (ie, name of attribute to be assigned to)
- * attrno target attribute number
- * indirection subscripts for target column, if any
- */
-void
-updateTargetListEntry(ParseState *pstate,
- TargetEntry *tle,
- char *colname,
- int attrno,
- List *indirection)
-{
- Oid type_id = exprType(tle->expr); /* type of value provided */
- Oid attrtype; /* type of target column */
- int32 attrtypmod;
- Resdom *resnode = tle->resdom;
- Relation rd = pstate->p_target_relation;
-
- Assert(rd != NULL);
- if (attrno <= 0)
- elog(ERROR, "Cannot assign to system attribute '%s'", colname);
- attrtype = attnumTypeId(rd, attrno);
- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
-
- /*
- * If there are subscripts on the target column, prepare an array
- * assignment expression. This will generate an array value that the
- * source value has been inserted into, which can then be placed in
- * the new tuple constructed by INSERT or UPDATE. Note that
- * transformArraySubscripts takes care of type coercion.
- */
- if (indirection)
- {
- Node *arrayBase;
- ArrayRef *aref;
-
- if (pstate->p_is_insert)
- {
- /*
- * The command is INSERT INTO table (arraycol[subscripts]) ...
- * so there is not really a source array value to work with.
- * Let the executor do something reasonable, if it can. Notice
- * that we force transformArraySubscripts to treat the
- * subscripting op as an array-slice op below, so the source
- * data will have been coerced to the array type.
- */
- arrayBase = NULL; /* signal there is no source array */
- }
- else
- {
- /*
- * Build a Var for the array to be updated.
- */
- arrayBase = (Node *) make_var(pstate,
- pstate->p_target_rangetblentry,
- attrno);
- }
-
- aref = transformArraySubscripts(pstate,
- arrayBase,
- attrtype,
- indirection,
- pstate->p_is_insert,
- tle->expr);
- tle->expr = (Node *) aref;
- }
- else
- {
- /*
- * For normal non-subscripted target column, do type checking and
- * coercion. But accept InvalidOid, which indicates the source is
- * a NULL constant.
- */
- if (type_id != InvalidOid)
- {
- if (type_id != attrtype)
- {
- tle->expr = CoerceTargetExpr(pstate, tle->expr, type_id,
- attrtype, attrtypmod,
- false);
- if (tle->expr == NULL)
- elog(ERROR, "column \"%s\" is of type '%s'"
- " but expression is of type '%s'"
- "\n\tYou will need to rewrite or cast the expression",
- colname,
- format_type_be(attrtype),
- format_type_be(type_id));
- }
-
- /*
- * If the target is a fixed-length type, it may need a length
- * coercion as well as a type coercion.
- */
- tle->expr = coerce_type_typmod(pstate, tle->expr,
- attrtype, attrtypmod);
- }
- }
-
- /*
- * The result of the target expression should now match the
- * destination column's type. Also, reset the resname and resno to
- * identify the destination column --- rewriter and planner depend on
- * that!
- */
- resnode->restype = attrtype;
- resnode->restypmod = attrtypmod;
- resnode->resname = colname;
- resnode->resno = (AttrNumber) attrno;
-}
-
-
-Node *
-CoerceTargetExpr(ParseState *pstate,
- Node *expr,
- Oid type_id,
- Oid attrtype,
- int32 attrtypmod,
- bool isExplicit)
-{
- if (can_coerce_type(1, &type_id, &attrtype, isExplicit))
- expr = coerce_type(pstate, expr, type_id, attrtype, attrtypmod,
- isExplicit);
-
-#ifndef DISABLE_STRING_HACKS
-
- /*
- * string hacks to get transparent conversions w/o explicit
- * conversions
- */
- else if ((attrtype == BPCHAROID) || (attrtype == VARCHAROID))
- {
- Oid text_id = TEXTOID;
-
- if (type_id == TEXTOID)
- {
- }
- else if (can_coerce_type(1, &type_id, &text_id, isExplicit))
- expr = coerce_type(pstate, expr, type_id, text_id, attrtypmod,
- isExplicit);
- else
- expr = NULL;
- }
-#endif
-
- else
- expr = NULL;
-
- return expr;
-}
-
-
-/*
- * checkInsertTargets -
- * generate a list of INSERT column targets if not supplied, or
- * test supplied column names to make sure they are in target table.
- * Also return an integer list of the columns' attribute numbers.
- */
-List *
-checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
-{
- *attrnos = NIL;
-
- if (cols == NIL)
- {
- /*
- * Generate default column list for INSERT.
- */
- Form_pg_attribute *attr = pstate->p_target_relation->rd_att->attrs;
- int numcol = pstate->p_target_relation->rd_rel->relnatts;
- int i;
-
- for (i = 0; i < numcol; i++)
- {
- ResTarget *col = makeNode(ResTarget);
-
- col->name = pstrdup(NameStr(attr[i]->attname));
- col->indirection = NIL;
- col->val = NULL;
- cols = lappend(cols, col);
- *attrnos = lappendi(*attrnos, i + 1);
- }
- }
- else
- {
- /*
- * Do initial validation of user-supplied INSERT column list.
- */
- List *tl;
-
- foreach(tl, cols)
- {
- char *name = ((ResTarget *) lfirst(tl))->name;
- int attrno;
-
- /* Lookup column name, elog on failure */
- attrno = attnameAttNum(pstate->p_target_relation, name);
- /* Check for duplicates */
- if (intMember(attrno, *attrnos))
- elog(ERROR, "Attribute '%s' specified more than once", name);
- *attrnos = lappendi(*attrnos, attrno);
- }
- }
-
- return cols;
-}
-
-/* ExpandAllTables()
- * Turns '*' (in the target list) into a list of targetlist entries.
- *
- * tlist entries are generated for each relation appearing at the top level
- * of the query's namespace, except for RTEs marked not inFromCl. (These
- * may include NEW/OLD pseudo-entries, implicit RTEs, etc.)
- */
-static List *
-ExpandAllTables(ParseState *pstate)
-{
- List *target = NIL;
- List *ns;
-
- foreach(ns, pstate->p_namespace)
- {
- Node *n = (Node *) lfirst(ns);
- RangeTblEntry *rte;
-
- if (IsA(n, RangeTblRef))
- rte = rt_fetch(((RangeTblRef *) n)->rtindex,
- pstate->p_rtable);
- else if (IsA(n, JoinExpr))
- rte = rt_fetch(((JoinExpr *) n)->rtindex,
- pstate->p_rtable);
- else
- {
- elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
- "\n\t%s", nodeToString(n));
- rte = NULL; /* keep compiler quiet */
- }
-
- /*
- * Ignore added-on relations that were not listed in the FROM
- * clause.
- */
- if (!rte->inFromCl)
- continue;
-
- target = nconc(target, expandRelAttrs(pstate, rte));
- }
-
- /* Check for SELECT *; */
- if (target == NIL)
- elog(ERROR, "Wildcard with no tables specified not allowed");
-
- return target;
-}
-
-/*
- * FigureColname -
- * if the name of the resulting column is not specified in the target
- * list, we have to guess a suitable name. The SQL spec provides some
- * guidance, but not much...
- *
- * Note that the argument is the *untransformed* parse tree for the target
- * item. This is a shade easier to work with than the transformed tree.
- */
-static char *
-FigureColname(Node *node)
-{
- char *name = NULL;
-
- FigureColnameInternal(node, &name);
- if (name != NULL)
- return name;
- /* default result if we can't guess anything */
- return "?column?";
-}
-
-static int
-FigureColnameInternal(Node *node, char **name)
-{
- int strength = 0;
-
- if (node == NULL)
- return strength;
-
- switch (nodeTag(node))
- {
- case T_Ident:
- *name = ((Ident *) node)->name;
- return 2;
- case T_ColumnRef:
- {
- char *cname = strVal(llast(((ColumnRef *) node)->fields));
-
- if (strcmp(cname, "*") != 0)
- {
- *name = cname;
- return 2;
- }
- }
- break;
- case T_ExprFieldSelect:
- {
- char *fname = strVal(llast(((ExprFieldSelect *) node)->fields));
-
- if (strcmp(fname, "*") != 0)
- {
- *name = fname;
- return 2;
- }
- }
- break;
- case T_FuncCall:
- *name = strVal(llast(((FuncCall *) node)->funcname));
- return 2;
- case T_A_Const:
- if (((A_Const *) node)->typename != NULL)
- {
- *name = strVal(llast(((A_Const *) node)->typename->names));
- return 1;
- }
- break;
- case T_TypeCast:
- strength = FigureColnameInternal(((TypeCast *) node)->arg,
- name);
- if (strength <= 1)
- {
- if (((TypeCast *) node)->typename != NULL)
- {
- *name = strVal(llast(((TypeCast *) node)->typename->names));
- return 1;
- }
- }
- break;
- case T_CaseExpr:
- strength = FigureColnameInternal(((CaseExpr *) node)->defresult,
- name);
- if (strength <= 1)
- {
- *name = "case";
- return 1;
- }
- break;
- default:
- break;
- }
-
- return strength;
-}