aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_clause.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>1999-07-19 00:26:20 +0000
committerTom Lane <tgl@sss.pgh.pa.us>1999-07-19 00:26:20 +0000
commit7f76eab140e703b7847b107245a669e2010886c0 (patch)
tree8c9d01c654aa8de0e14c0d446817ba60c33d0199 /src/backend/parser/parse_clause.c
parentc9814427722798751fa5bf254f597d722d76b5e3 (diff)
downloadpostgresql-7f76eab140e703b7847b107245a669e2010886c0.tar.gz
postgresql-7f76eab140e703b7847b107245a669e2010886c0.zip
Rewrite parser's handling of INSERT ... SELECT so that processing
of the SELECT part of the statement is just like a plain SELECT. All INSERT-specific processing happens after the SELECT parsing is done. This eliminates many problems, e.g. INSERT ... SELECT ... GROUP BY using the wrong column labels. Ensure that DEFAULT clauses are coerced to the target column type, whether or not stored clause produces the right type. Substantial cleanup of parser's array support.
Diffstat (limited to 'src/backend/parser/parse_clause.c')
-rw-r--r--src/backend/parser/parse_clause.c349
1 files changed, 136 insertions, 213 deletions
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index ce9583c174a..f8dc7777b06 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -7,12 +7,13 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.41 1999/07/17 20:17:23 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.42 1999/07/19 00:26:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+
#include "access/heapam.h"
#include "nodes/relation.h"
#include "parse.h"
@@ -29,34 +30,39 @@
static char *clauseText[] = {"ORDER", "GROUP"};
-static TargetEntry *
- findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause);
-
+static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
+ List *tlist, int clause);
static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
+static char *transformTableEntry(ParseState *pstate, RangeVar *r);
#ifdef ENABLE_OUTER_JOINS
-Node *transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname);
-
+static Node *transformUsingClause(ParseState *pstate, List *onList,
+ char *lname, char *rname);
#endif
-static char *transformTableEntry(ParseState *pstate, RangeVar *r);
/*
* makeRangeTable -
- * make a range table with the specified relation (optional) and the
- * from_clause.
+ * Build the initial range table from the FROM clause.
*/
void
-makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual)
+makeRangeTable(ParseState *pstate, List *frmList, Node **qual)
{
- RangeTblEntry *rte;
- int sublevels_up;
-
+ /* Currently, nothing to do except this: */
parseFromClause(pstate, frmList, qual);
+}
- if (relname == NULL)
- return;
+/*
+ * setTargetTable
+ * Add the target relation of INSERT or UPDATE to the range table,
+ * and make the special links to it in the ParseState.
+ */
+void
+setTargetTable(ParseState *pstate, char *relname)
+{
+ RangeTblEntry *rte;
+ int sublevels_up;
if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0)
|| (sublevels_up != 0))
@@ -136,7 +142,7 @@ makeAttr(char *relname, char *attname)
/* transformUsingClause()
* Take an ON or USING clause from a join expression and expand if necessary.
*/
-Node *
+static Node *
transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname)
{
A_Expr *expr = NULL;
@@ -295,7 +301,8 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
if (IsA(j->quals, List))
j->quals = lcons(transformUsingClause(pstate, (List *) j->quals, lname, rname), NIL);
- Assert(qual != NULL);
+ if (qual == NULL)
+ elog(ERROR, "JOIN/ON not supported in this context");
if (*qual == NULL)
*qual = lfirst(j->quals);
@@ -329,145 +336,111 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
}
}
+
/*
* findTargetlistEntry -
- * returns the Resdom in the target list matching the specified varname
- * and range. If none exist one is created.
- *
- * Rewritten for ver 6.4 to handle expressions in the GROUP/ORDER BY clauses.
- * - daveh@insightdist.com 1998-07-31
+ * Returns the targetlist entry matching the given (untransformed) node.
+ * If no matching entry exists, one is created and appended to the target
+ * list as a "resjunk" node.
*
+ * node the ORDER BY or GROUP BY expression to be matched
+ * tlist the existing target list (NB: this cannot be NIL, which is a
+ * good thing since we'd be unable to append to it...)
+ * clause identifies clause type for error messages.
*/
static TargetEntry *
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
{
- List *l;
- int rtable_pos = 0,
- target_pos = 0,
- targetlist_pos = 0;
TargetEntry *target_result = NULL;
- Value *val = NULL;
- char *relname = NULL;
- char *name = NULL;
- Node *expr = NULL;
- int relCnt = 0;
-
- /* Pull out some values before looping thru target list */
- switch (nodeTag(node))
- {
- case T_Attr:
- relname = ((Attr *) node)->relname;
- val = (Value *) lfirst(((Attr *) node)->attrs);
- name = strVal(val);
- rtable_pos = refnameRangeTablePosn(pstate, relname, NULL);
- relCnt = length(pstate->p_rtable);
- break;
-
- case T_Ident:
- name = ((Ident *) node)->name;
- relCnt = length(pstate->p_rtable);
- break;
-
- case T_A_Const:
- val = &((A_Const *) node)->val;
-
- if (nodeTag(val) != T_Integer)
- elog(ERROR, "Illegal Constant in %s BY", clauseText[clause]);
- target_pos = intVal(val);
- break;
-
- case T_FuncCall:
- case T_A_Expr:
- expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
- break;
-
- default:
- elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
- }
+ List *tl;
+ Node *expr;
- /*
- * Loop through target entries and try to match to node
+ /*----------
+ * Handle two special cases as mandated by the SQL92 spec:
+ *
+ * 1. ORDER/GROUP BY ColumnName
+ * For a bare identifier, we search for a matching column name
+ * in the existing target list. Multiple matches are an error
+ * unless they refer to identical values; for example,
+ * we allow SELECT a, a FROM table ORDER BY a
+ * but not SELECT a AS b, b FROM table ORDER BY b
+ * If no match is found, we fall through and treat the identifier
+ * as an expression.
+ *
+ * 2. ORDER/GROUP BY IntegerConstant
+ * This means to use the n'th item in the existing target list.
+ * Note that it would make no sense to order/group by an actual
+ * constant, so this does not create a conflict with our extension
+ * to order/group by an expression.
+ *
+ * Note that pre-existing resjunk targets must not be used in either case.
+ *----------
*/
- foreach(l, tlist)
+ if (IsA(node, Ident) && ((Ident *) node)->indirection == NIL)
{
- TargetEntry *target = (TargetEntry *) lfirst(l);
- Resdom *resnode = target->resdom;
- Var *var = (Var *) target->expr;
- char *resname = resnode->resname;
- int test_rtable_pos = var->varno;
-
- ++targetlist_pos;
-
- switch (nodeTag(node))
+ char *name = ((Ident *) node)->name;
+ foreach(tl, tlist)
{
- case T_Attr:
- if (strcmp(resname, name) == 0 && rtable_pos == test_rtable_pos)
- {
-
- /*
- * Check for only 1 table & ORDER BY -ambiguity does
- * not matter here
- */
- if (clause == ORDER_CLAUSE && relCnt == 1)
- return target;
-
- if (target_result != NULL)
- elog(ERROR, "%s BY '%s' is ambiguous", clauseText[clause], name);
- else
- target_result = target;
- /* Stay in loop to check for ambiguity */
- }
- break;
-
- case T_Ident:
- if (strcmp(resname, name) == 0)
- {
+ TargetEntry *tle = (TargetEntry *) lfirst(tl);
+ Resdom *resnode = tle->resdom;
- /*
- * Check for only 1 table & ORDER BY -ambiguity does
- * not matter here
- */
- if (clause == ORDER_CLAUSE && relCnt == 1)
- return target;
-
- if (target_result != NULL)
- elog(ERROR, "%s BY '%s' is ambiguous", clauseText[clause], name);
- else
- target_result = target;
- /* Stay in loop to check for ambiguity */
- }
- break;
-
- case T_A_Const:
- if (target_pos == targetlist_pos)
+ if (!resnode->resjunk &&
+ strcmp(resnode->resname, name) == 0)
+ {
+ if (target_result != NULL)
{
- /* Can't be ambigious and we got what we came for */
- return target;
+ if (! equal(target_result->expr, tle->expr))
+ elog(ERROR, "%s BY '%s' is ambiguous",
+ clauseText[clause], name);
}
- break;
+ else
+ target_result = tle;
+ /* Stay in loop to check for ambiguity */
+ }
+ }
+ if (target_result != NULL)
+ return target_result; /* return the first match */
+ }
+ if (IsA(node, A_Const))
+ {
+ Value *val = &((A_Const *) node)->val;
+ int targetlist_pos = 0;
+ int target_pos;
+
+ if (nodeTag(val) != T_Integer)
+ elog(ERROR, "Non-integer constant in %s BY", clauseText[clause]);
+ target_pos = intVal(val);
+ foreach(tl, tlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tl);
+ Resdom *resnode = tle->resdom;
- case T_FuncCall:
- case T_A_Expr:
- if (equal(expr, target->expr))
- {
+ if (!resnode->resjunk)
+ {
+ if (++targetlist_pos == target_pos)
+ return tle; /* return the unique match */
+ }
+ }
+ elog(ERROR, "%s BY position %d is not in target list",
+ clauseText[clause], target_pos);
+ }
- /*
- * Check for only 1 table & ORDER BY -ambiguity does
- * not matter here
- */
- if (clause == ORDER_CLAUSE)
- return target;
+ /*
+ * Otherwise, we have an expression (this is a Postgres extension
+ * not found in SQL92). Convert the untransformed node to a
+ * transformed expression, and search for a match in the tlist.
+ * NOTE: it doesn't really matter whether there is more than one
+ * match. Also, we are willing to match a resjunk target here,
+ * though the above cases must ignore resjunk targets.
+ */
+ expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
- if (target_result != NULL)
- elog(ERROR, "GROUP BY has ambiguous expression");
- else
- target_result = target;
- }
- break;
+ foreach(tl, tlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tl);
- default:
- elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
- }
+ if (equal(expr, tle->expr))
+ return tle;
}
/*
@@ -475,51 +448,13 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
* the end of the target list. This target is set to be resjunk =
* TRUE so that it will not be projected into the final tuple.
*/
- if (target_result == NULL)
- {
- switch (nodeTag(node))
- {
- case T_Attr:
- target_result = MakeTargetEntryIdent(pstate, node,
- &((Attr *) node)->relname, NULL,
- ((Attr *) node)->relname, true);
- lappend(tlist, target_result);
- break;
-
- case T_Ident:
- target_result = MakeTargetEntryIdent(pstate, node,
- &((Ident *) node)->name, NULL,
- ((Ident *) node)->name, true);
- lappend(tlist, target_result);
- break;
-
- case T_A_Const:
-
- /*
- * If we got this far, then must have been an out-of-range
- * column number
- */
- elog(ERROR, "%s BY position %d is not in target list", clauseText[clause], target_pos);
- break;
-
- case T_FuncCall:
- case T_A_Expr:
- target_result = MakeTargetEntryExpr(pstate, "resjunk", expr, false, true);
- lappend(tlist, target_result);
- break;
-
- default:
- elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
- break;
- }
- }
+ target_result = transformTargetEntry(pstate, node, expr, NULL, true);
+ lappend(tlist, target_result);
return target_result;
}
-
-
/*
* transformGroupClause -
* transform a Group By clause
@@ -529,55 +464,42 @@ List *
transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
{
List *glist = NIL,
- *gl = NIL;
+ *gl,
+ *othergl;
+ int nextgroupref = 1;
- while (grouplist != NIL)
+ foreach(gl, grouplist)
{
- GroupClause *grpcl = makeNode(GroupClause);
TargetEntry *restarget;
Resdom *resdom;
- restarget = findTargetlistEntry(pstate, lfirst(grouplist), targetlist, GROUP_CLAUSE);
-
+ restarget = findTargetlistEntry(pstate, lfirst(gl),
+ targetlist, GROUP_CLAUSE);
resdom = restarget->resdom;
- grpcl->grpOpoid = oprid(oper("<",
- resdom->restype,
- resdom->restype, false));
- if (glist == NIL)
- {
- int groupref = length(glist) + 1;
- restarget->resdom->resgroupref = groupref;
- grpcl->tleGroupref = groupref;
+ /* avoid making duplicate grouplist entries */
+ foreach(othergl, glist)
+ {
+ GroupClause *gcl = (GroupClause *) lfirst(othergl);
- gl = glist = lcons(grpcl, NIL);
+ if (equal(get_groupclause_expr(gcl, targetlist),
+ restarget->expr))
+ break;
}
- else
- {
- List *i;
- foreach(i, glist)
- {
- GroupClause *gcl = (GroupClause *) lfirst(i);
+ if (othergl == NIL) /* not in grouplist already */
+ {
+ GroupClause *grpcl = makeNode(GroupClause);
- if (equal(get_groupclause_expr(gcl, targetlist),
- restarget->expr))
- break;
- }
- if (i == NIL) /* not in grouplist already */
- {
- int groupref = length(glist) + 1;
+ grpcl->tleGroupref = nextgroupref++;
+ resdom->resgroupref = grpcl->tleGroupref;
- restarget->resdom->resgroupref = groupref;
- grpcl->tleGroupref = groupref;
+ grpcl->grpOpoid = oprid(oper("<",
+ resdom->restype,
+ resdom->restype, false));
- lnext(gl) = lcons(grpcl, NIL);
- gl = lnext(gl);
- }
- else
- pfree(grpcl); /* get rid of this */
+ glist = lappend(glist, grpcl);
}
- grouplist = lnext(grouplist);
}
return glist;
@@ -604,7 +526,8 @@ transformSortClause(ParseState *pstate,
TargetEntry *restarget;
Resdom *resdom;
- restarget = findTargetlistEntry(pstate, sortby->node, targetlist, ORDER_CLAUSE);
+ restarget = findTargetlistEntry(pstate, sortby->node,
+ targetlist, ORDER_CLAUSE);
sortcl->resdom = resdom = restarget->resdom;