diff options
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r-- | src/backend/parser/analyze.c | 163 |
1 files changed, 117 insertions, 46 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 5dc3d82bea8..c2159a70e0e 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.267 2003/04/29 03:21:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.268 2003/04/29 22:13:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -84,6 +84,7 @@ typedef struct } CreateStmtContext; +static List *do_parse_analyze(Node *parseTree, ParseState *pstate); static Query *transformStmt(ParseState *pstate, Node *stmt, List **extras_before, List **extras_after); static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); @@ -125,10 +126,12 @@ static void release_pstate_resources(ParseState *pstate); static FromExpr *makeFromExpr(List *fromlist, Node *quals); - /* - * parse_analyze - - * analyze a raw parse tree and transform it to Query form. + * parse_analyze + * Analyze a raw parse tree and transform it to Query form. + * + * Optionally, information about $n parameter types can be supplied. + * References to $n indexes not defined by paramTypes[] are disallowed. * * The result is a List of Query nodes (we need a list since some commands * produce multiple Queries). Optimizable statements require considerable @@ -136,11 +139,74 @@ static FromExpr *makeFromExpr(List *fromlist, Node *quals); * a dummy CMD_UTILITY Query node. */ List * -parse_analyze(Node *parseTree, ParseState *parentParseState) +parse_analyze(Node *parseTree, Oid *paramTypes, int numParams) +{ + ParseState *pstate = make_parsestate(NULL); + List *result; + + pstate->p_paramtypes = paramTypes; + pstate->p_numparams = numParams; + pstate->p_variableparams = false; + + result = do_parse_analyze(parseTree, pstate); + + pfree(pstate); + + return result; +} + +/* + * parse_analyze_varparams + * + * This variant is used when it's okay to deduce information about $n + * symbol datatypes from context. The passed-in paramTypes[] array can + * be modified or enlarged (via repalloc). + */ +List * +parse_analyze_varparams(Node *parseTree, Oid **paramTypes, int *numParams) +{ + ParseState *pstate = make_parsestate(NULL); + List *result; + + pstate->p_paramtypes = *paramTypes; + pstate->p_numparams = *numParams; + pstate->p_variableparams = true; + + result = do_parse_analyze(parseTree, pstate); + + *paramTypes = pstate->p_paramtypes; + *numParams = pstate->p_numparams; + + pfree(pstate); + + return result; +} + +/* + * parse_sub_analyze + * Entry point for recursively analyzing a sub-statement. + */ +List * +parse_sub_analyze(Node *parseTree, ParseState *parentParseState) { - List *result = NIL; ParseState *pstate = make_parsestate(parentParseState); + List *result; + + result = do_parse_analyze(parseTree, pstate); + + pfree(pstate); + return result; +} + +/* + * do_parse_analyze + * Workhorse code shared by the above variants of parse_analyze. + */ +static List * +do_parse_analyze(Node *parseTree, ParseState *pstate) +{ + List *result = NIL; /* Lists to return extra commands from transformation */ List *extras_before = NIL; List *extras_after = NIL; @@ -148,11 +214,14 @@ parse_analyze(Node *parseTree, ParseState *parentParseState) List *listscan; query = transformStmt(pstate, parseTree, &extras_before, &extras_after); + + /* don't need to access result relation any more */ release_pstate_resources(pstate); while (extras_before != NIL) { - result = nconc(result, parse_analyze(lfirst(extras_before), pstate)); + result = nconc(result, + parse_sub_analyze(lfirst(extras_before), pstate)); extras_before = lnext(extras_before); } @@ -160,13 +229,14 @@ parse_analyze(Node *parseTree, ParseState *parentParseState) while (extras_after != NIL) { - result = nconc(result, parse_analyze(lfirst(extras_after), pstate)); + result = nconc(result, + parse_sub_analyze(lfirst(extras_after), pstate)); extras_after = lnext(extras_after); } /* * Make sure that only the original query is marked original. We have - * to do this explicitly since recursive calls of parse_analyze will + * to do this explicitly since recursive calls of do_parse_analyze will * have marked some of the added-on queries as "original". */ foreach(listscan, result) @@ -176,8 +246,6 @@ parse_analyze(Node *parseTree, ParseState *parentParseState) q->querySource = (q == query ? QSRC_ORIGINAL : QSRC_PARSER); } - pfree(pstate); - return result; } @@ -423,7 +491,14 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, */ if (stmt->selectStmt) { - ParseState *sub_pstate = make_parsestate(pstate->parentParseState); + /* + * We make the sub-pstate a child of the outer pstate so that it + * can see any Param definitions supplied from above. Since the + * outer pstate's rtable and namespace are presently empty, there + * are no side-effects of exposing names the sub-SELECT shouldn't + * be able to see. + */ + ParseState *sub_pstate = make_parsestate(pstate); Query *selectQuery; RangeTblEntry *rte; RangeTblRef *rtr; @@ -475,12 +550,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, * separate from the subquery's tlist because we may add columns, * insert datatype coercions, etc.) * - * HACK: constants in the INSERT's targetlist are copied up as-is - * rather than being referenced as subquery outputs. This is - * mainly to ensure that when we try to coerce them to the target - * column's datatype, the right things happen for UNKNOWN - * constants. Otherwise this fails: INSERT INTO foo SELECT 'bar', - * ... FROM baz + * HACK: unknown-type constants and params in the INSERT's targetlist + * are copied up as-is rather than being referenced as subquery + * outputs. This is to ensure that when we try to coerce them + * to the target column's datatype, the right things happen (see + * special cases in coerce_type). Otherwise, this fails: + * INSERT INTO foo SELECT 'bar', ... FROM baz */ qry->targetList = NIL; foreach(tl, selectQuery->targetList) @@ -491,7 +566,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, if (resnode->resjunk) continue; - if (tle->expr && IsA(tle->expr, Const)) + if (tle->expr && + (IsA(tle->expr, Const) || IsA(tle->expr, Param)) && + exprType((Node *) tle->expr) == UNKNOWNOID) expr = tle->expr; else expr = (Expr *) makeVar(rtr->rtindex, @@ -500,7 +577,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, resnode->restypmod, 0); resnode = copyObject(resnode); - resnode->resno = (AttrNumber) pstate->p_last_resno++; + resnode->resno = (AttrNumber) pstate->p_next_resno++; qry->targetList = lappend(qry->targetList, makeTargetEntry(resnode, expr)); } @@ -520,8 +597,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, */ /* Prepare to assign non-conflicting resnos to resjunk attributes */ - if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts) - pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1; + if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts) + pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1; /* Validate stmt->cols list, or build default list if no list given */ icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); @@ -1484,7 +1561,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt, List *newactions = NIL; /* - * transform each statement, like parse_analyze() + * transform each statement, like parse_sub_analyze() */ foreach(oldactions, stmt->actions) { @@ -1789,7 +1866,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) Resdom *resdom; Expr *expr; - resdom = makeResdom((AttrNumber) pstate->p_last_resno++, + resdom = makeResdom((AttrNumber) pstate->p_next_resno++, colType, -1, colName, @@ -1938,7 +2015,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt) * of this sub-query, because they are not in the toplevel * pstate's namespace list. */ - selectList = parse_analyze((Node *) stmt, pstate); + selectList = parse_sub_analyze((Node *) stmt, pstate); Assert(length(selectList) == 1); selectQuery = (Query *) lfirst(selectList); @@ -2132,8 +2209,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) */ /* Prepare to assign non-conflicting resnos to resjunk attributes */ - if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts) - pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1; + if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts) + pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1; /* Prepare non-junk columns for assignment to target table */ origTargetList = stmt->targetList; @@ -2151,7 +2228,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) * columns; else rewriter or planner might get confused. */ resnode->resname = "?resjunk?"; - resnode->resno = (AttrNumber) pstate->p_last_resno++; + resnode->resno = (AttrNumber) pstate->p_next_resno++; continue; } if (origTargetList == NIL) @@ -2316,11 +2393,10 @@ static Query * transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt) { Query *result = makeNode(Query); - List *extras_before = NIL, - *extras_after = NIL; List *argtype_oids = NIL; /* argtype OIDs in a list */ - Oid *argtoids = NULL; /* as an array for parser_param_set */ + Oid *argtoids = NULL; /* and as an array */ int nargs; + List *queries; result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) stmt; @@ -2348,24 +2424,19 @@ transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt) stmt->argtype_oids = argtype_oids; /* - * We need to adjust the parameters expected by the rest of the - * system, so that $1, ... $n are parsed properly. - * - * This is somewhat of a hack; however, the main parser interface only - * allows parameters to be specified when working with a raw query - * string, which is not helpful here. + * Analyze the statement using these parameter types (any parameters + * passed in from above us will not be visible to it). */ - parser_param_set(argtoids, nargs); - - stmt->query = transformStmt(pstate, (Node *) stmt->query, - &extras_before, &extras_after); + queries = parse_analyze((Node *) stmt->query, argtoids, nargs); - /* Shouldn't get any extras, since grammar only allows OptimizableStmt */ - if (extras_before || extras_after) + /* + * Shouldn't get any extra statements, since grammar only allows + * OptimizableStmt + */ + if (length(queries) != 1) elog(ERROR, "transformPrepareStmt: internal error"); - /* Remove links to our local parameters */ - parser_param_set(NULL, 0); + stmt->query = lfirst(queries); return result; } @@ -2409,7 +2480,7 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt) given_type_id = exprType(expr); expected_type_id = lfirsto(paramtypes); - expr = coerce_to_target_type(expr, given_type_id, + expr = coerce_to_target_type(pstate, expr, given_type_id, expected_type_id, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); |