diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2003-04-29 22:13:11 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2003-04-29 22:13:11 +0000 |
commit | aa282d44464df0fbfa0672dc353d36734ec1092e (patch) | |
tree | dbf42be31346c6716bc33e73e801cda670fc60e4 /src | |
parent | 19141f558411e96446294baf240eaeccf6d68b64 (diff) | |
download | postgresql-aa282d44464df0fbfa0672dc353d36734ec1092e.tar.gz postgresql-aa282d44464df0fbfa0672dc353d36734ec1092e.zip |
Infrastructure for deducing Param types from context, in the same way
that the types of untyped string-literal constants are deduced (ie,
when coerce_type is applied to 'em, that's what the type must be).
Remove the ancient hack of storing the input Param-types array as a
global variable, and put the info into ParseState instead. This touches
a lot of files because of adjustment of routine parameter lists, but
it's really not a large patch. Note: PREPARE statement still insists on
exact specification of parameter types, but that could easily be relaxed
now, if we wanted to do so.
Diffstat (limited to 'src')
29 files changed, 441 insertions, 246 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 802f5932f73..5a6ec98e1b8 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.241 2003/03/23 05:14:36 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.242 2003/04/29 22:13:08 tgl Exp $ * * * INTERFACE ROUTINES @@ -1592,7 +1592,7 @@ AddRelationRawConstraints(Relation rel, /* * Make sure it yields a boolean result. */ - expr = coerce_to_boolean(expr, "CHECK"); + expr = coerce_to_boolean(pstate, expr, "CHECK"); /* * Make sure no outside relations are referred to. @@ -1743,7 +1743,7 @@ cookDefault(ParseState *pstate, { Oid type_id = exprType(expr); - if (coerce_to_target_type(expr, type_id, + if (coerce_to_target_type(pstate, expr, type_id, atttypid, atttypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST) == NULL) diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index ba37c249873..4e2224e189b 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.7 2002/12/05 04:04:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.8 2003/04/29 22:13:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -124,7 +124,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt) List *querytree_list, *querytree_item; - querytree_list = parse_analyze(parsetree, NULL); + querytree_list = parse_analyze(parsetree, NULL, 0); foreach(querytree_item, querytree_list) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index b4576893c66..5f60ab9cf01 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.71 2003/04/21 15:19:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.72 2003/04/29 22:13:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2852,7 +2852,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr) /* * Make sure it yields a boolean result. */ - expr = coerce_to_boolean(expr, "CHECK"); + expr = coerce_to_boolean(pstate, expr, "CHECK"); /* * Make sure no outside relations are referred to. diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 3cc11ed81a5..0523878f2a7 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.33 2003/04/08 16:57:45 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.34 2003/04/29 22:13:08 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -1601,7 +1601,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, /* * Make sure it yields a boolean result. */ - expr = coerce_to_boolean(expr, "CHECK"); + expr = coerce_to_boolean(pstate, expr, "CHECK"); /* * Make sure no outside relations are diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 218b56a6013..aca27d0aebf 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.92 2003/04/29 03:21:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.93 2003/04/29 22:13:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -993,7 +993,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan) /* * Parse the request string into a list of raw parse trees. */ - raw_parsetree_list = pg_parse_query(src, argtypes, nargs); + raw_parsetree_list = pg_parse_query(src); /* * Do parse analysis and rule rewrite for each raw parsetree. @@ -1036,7 +1036,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan) if (plan) plan->origCmdType = origCmdType; - query_list = pg_analyze_and_rewrite(parsetree); + query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs); query_list_list = lappend(query_list_list, query_list); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 15d16d00221..4be69d77cfd 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.74 2003/04/08 23:20:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.75 2003/04/29 22:13:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -495,10 +495,13 @@ convert_sublink_opers(List *lefthand, List *operOids, * Make the expression node. * * Note: we use make_op_expr in case runtime type conversion - * function calls must be inserted for this operator! + * function calls must be inserted for this operator! (But we + * are not expecting to have to resolve unknown Params, so + * it's okay to pass a null pstate.) */ result = lappend(result, - make_op_expr(tup, + make_op_expr(NULL, + tup, leftop, rightop, exprType(leftop), diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index fb7bf9e7070..d2b91c2ec6d 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.93 2003/04/24 23:43:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.94 2003/04/29 22:13:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -418,7 +418,8 @@ generate_setop_tlist(List *colTypes, int flag, } else { - expr = coerce_to_common_type(expr, + expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */ + expr, colType, "UNION/INTERSECT/EXCEPT"); colTypmod = -1; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 778fc3a2bb9..c0ffd939cbe 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.135 2003/04/27 20:09:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.136 2003/04/29 22:13:09 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1747,17 +1747,17 @@ inline_function(Oid funcid, Oid result_type, List *args, /* * We just do parsing and parse analysis, not rewriting, because - * rewriting will not affect SELECT-only queries, which is all that - * we care about. Also, we can punt as soon as we detect more than + * rewriting will not affect table-free-SELECT-only queries, which is all + * that we care about. Also, we can punt as soon as we detect more than * one command in the function body. */ - raw_parsetree_list = pg_parse_query(src, - funcform->proargtypes, - funcform->pronargs); + raw_parsetree_list = pg_parse_query(src); if (length(raw_parsetree_list) != 1) goto fail; - querytree_list = parse_analyze(lfirst(raw_parsetree_list), NULL); + querytree_list = parse_analyze(lfirst(raw_parsetree_list), + funcform->proargtypes, + funcform->pronargs); if (length(querytree_list) != 1) goto fail; 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); diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 2fd5811000a..1c8cb8bc0e3 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.112 2003/03/22 01:49:38 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.113 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -54,7 +54,7 @@ static RangeTblRef *transformRangeFunction(ParseState *pstate, RangeFunction *r); static Node *transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels); -static Node *buildMergedJoinVar(JoinType jointype, +static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, Var *l_colvar, Var *r_colvar); static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause); @@ -284,7 +284,7 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars) */ result = transformExpr(pstate, result); - result = coerce_to_boolean(result, "JOIN/USING"); + result = coerce_to_boolean(pstate, result, "JOIN/USING"); return result; } /* transformJoinUsingClause() */ @@ -318,7 +318,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, /* This part is just like transformWhereClause() */ result = transformExpr(pstate, j->quals); - result = coerce_to_boolean(result, "JOIN/ON"); + result = coerce_to_boolean(pstate, result, "JOIN/ON"); pstate->p_namespace = save_namespace; @@ -398,7 +398,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) /* * Analyze and transform the subquery. */ - parsetrees = parse_analyze(r->subquery, pstate); + parsetrees = parse_sub_analyze(r->subquery, pstate); /* * Check that we got something reasonable. Some of these conditions @@ -759,7 +759,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) res_colnames = lappend(res_colnames, lfirst(ucol)); res_colvars = lappend(res_colvars, - buildMergedJoinVar(j->jointype, + buildMergedJoinVar(pstate, + j->jointype, l_colvar, r_colvar)); } @@ -836,7 +837,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) * generate a suitable replacement expression for a merged join column */ static Node * -buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar) +buildMergedJoinVar(ParseState *pstate, JoinType jointype, + Var *l_colvar, Var *r_colvar) { Oid outcoltype; int32 outcoltypmod; @@ -869,7 +871,7 @@ buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar) * typmod is not same as input. */ if (l_colvar->vartype != outcoltype) - l_node = coerce_type((Node *) l_colvar, l_colvar->vartype, + l_node = coerce_type(pstate, (Node *) l_colvar, l_colvar->vartype, outcoltype, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else if (l_colvar->vartypmod != outcoltypmod) @@ -880,7 +882,7 @@ buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar) l_node = (Node *) l_colvar; if (r_colvar->vartype != outcoltype) - r_node = coerce_type((Node *) r_colvar, r_colvar->vartype, + r_node = coerce_type(pstate, (Node *) r_colvar, r_colvar->vartype, outcoltype, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else if (r_colvar->vartypmod != outcoltypmod) @@ -953,7 +955,7 @@ transformWhereClause(ParseState *pstate, Node *clause) qual = transformExpr(pstate, clause); - qual = coerce_to_boolean(qual, "WHERE"); + qual = coerce_to_boolean(pstate, qual, "WHERE"); return qual; } diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index a4b739b0f7e..9dc0c7f1c19 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.95 2003/04/10 02:47:46 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.96 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "catalog/pg_cast.h" #include "catalog/pg_proc.h" #include "nodes/makefuncs.h" +#include "nodes/params.h" #include "optimizer/clauses.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" @@ -49,6 +50,7 @@ static Node *build_func_call(Oid funcid, Oid rettype, List *args, * conversion is not possible. (We do this, rather than elog'ing directly, * so that callers can generate custom error messages indicating context.) * + * pstate - parse state (can be NULL, see coerce_type) * expr - input expression tree (already transformed by transformExpr) * exprtype - result type of expr * targettype - desired result type @@ -56,13 +58,13 @@ static Node *build_func_call(Oid funcid, Oid rettype, List *args, * ccontext, cformat - context indicators to control coercions */ Node * -coerce_to_target_type(Node *expr, Oid exprtype, +coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat) { if (can_coerce_type(1, &exprtype, &targettype, ccontext)) - expr = coerce_type(expr, exprtype, targettype, + expr = coerce_type(pstate, expr, exprtype, targettype, ccontext, cformat); /* * String hacks to get transparent conversions for char and varchar: @@ -79,7 +81,7 @@ coerce_to_target_type(Node *expr, Oid exprtype, if (can_coerce_type(1, &exprtype, &text_id, ccontext)) { - expr = coerce_type(expr, exprtype, text_id, + expr = coerce_type(pstate, expr, exprtype, text_id, ccontext, cformat); /* Need a RelabelType if no typmod coercion is performed */ if (targettypmod < 0) @@ -117,9 +119,14 @@ coerce_to_target_type(Node *expr, Oid exprtype, * call coerce_type_typmod as well, if a typmod constraint is wanted. * (But if the target type is a domain, it may internally contain a * typmod constraint, which will be applied inside coerce_to_domain.) + * + * pstate is only used in the case that we are able to resolve the type of + * a previously UNKNOWN Param. It is okay to pass pstate = NULL if the + * caller does not want type information updated for Params. */ Node * -coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId, +coerce_type(ParseState *pstate, Node *node, + Oid inputTypeId, Oid targetTypeId, CoercionContext ccontext, CoercionForm cformat) { Node *result; @@ -129,9 +136,9 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId, node == NULL) { /* no conversion needed */ - result = node; + return node; } - else if (inputTypeId == UNKNOWNOID && IsA(node, Const)) + if (inputTypeId == UNKNOWNOID && IsA(node, Const)) { /* * Input is a string constant with previously undetermined type. @@ -187,17 +194,62 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId, cformat); ReleaseSysCache(targetType); + + return result; } - else if (targetTypeId == ANYOID || - targetTypeId == ANYARRAYOID || - targetTypeId == ANYELEMENTOID) + if (inputTypeId == UNKNOWNOID && IsA(node, Param) && + ((Param *) node)->paramkind == PARAM_NUM && + pstate != NULL && pstate->p_variableparams) + { + /* + * Input is a Param of previously undetermined type, and we want + * to update our knowledge of the Param's type. Find the topmost + * ParseState and update the state. + */ + Param *param = (Param *) node; + int paramno = param->paramid; + ParseState *toppstate; + + toppstate = pstate; + while (toppstate->parentParseState != NULL) + toppstate = toppstate->parentParseState; + + if (paramno <= 0 || /* shouldn't happen, but... */ + paramno > toppstate->p_numparams) + elog(ERROR, "Parameter '$%d' is out of range", paramno); + + if (toppstate->p_paramtypes[paramno-1] == UNKNOWNOID) + { + /* We've successfully resolved the type */ + toppstate->p_paramtypes[paramno-1] = targetTypeId; + } + else if (toppstate->p_paramtypes[paramno-1] == targetTypeId) + { + /* We previously resolved the type, and it matches */ + } + else + { + /* Ooops */ + elog(ERROR, "Inconsistent types deduced for parameter '$%d'" + "\n\tCould be either %s or %s", + paramno, + format_type_be(toppstate->p_paramtypes[paramno-1]), + format_type_be(targetTypeId)); + } + + param->paramtype = targetTypeId; + return (Node *) param; + } + if (targetTypeId == ANYOID || + targetTypeId == ANYARRAYOID || + targetTypeId == ANYELEMENTOID) { /* assume can_coerce_type verified that implicit coercion is okay */ /* NB: we do NOT want a RelabelType here */ - result = node; + return node; } - else if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext, - &funcId)) + if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext, + &funcId)) { if (OidIsValid(funcId)) { @@ -247,27 +299,23 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId, cformat); } } + return result; } - else if (typeInheritsFrom(inputTypeId, targetTypeId)) + if (typeInheritsFrom(inputTypeId, targetTypeId)) { /* * Input class type is a subclass of target, so nothing to do --- * except relabel the type. This is binary compatibility for * complex types. */ - result = (Node *) makeRelabelType((Expr *) node, - targetTypeId, -1, - cformat); + return (Node *) makeRelabelType((Expr *) node, + targetTypeId, -1, + cformat); } - else - { - /* If we get here, caller blew it */ - elog(ERROR, "coerce_type: no conversion function from %s to %s", - format_type_be(inputTypeId), format_type_be(targetTypeId)); - result = NULL; /* keep compiler quiet */ - } - - return result; + /* If we get here, caller blew it */ + elog(ERROR, "coerce_type: no conversion function from %s to %s", + format_type_be(inputTypeId), format_type_be(targetTypeId)); + return NULL; /* keep compiler quiet */ } @@ -484,15 +532,19 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, * (AND, OR, NOT, etc). Also check that input is not a set. * * Returns the possibly-transformed node tree. + * + * As with coerce_type, pstate may be NULL if no special unknown-Param + * processing is wanted. */ Node * -coerce_to_boolean(Node *node, const char *constructName) +coerce_to_boolean(ParseState *pstate, Node *node, + const char *constructName) { Oid inputTypeId = exprType(node); if (inputTypeId != BOOLOID) { - node = coerce_to_target_type(node, inputTypeId, + node = coerce_to_target_type(pstate, node, inputTypeId, BOOLOID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); @@ -594,16 +646,20 @@ select_common_type(List *typeids, const char *context) * This is used following select_common_type() to coerce the individual * expressions to the desired type. 'context' is a phrase to use in the * error message if we fail to coerce. + * + * As with coerce_type, pstate may be NULL if no special unknown-Param + * processing is wanted. */ Node * -coerce_to_common_type(Node *node, Oid targetTypeId, const char *context) +coerce_to_common_type(ParseState *pstate, Node *node, + Oid targetTypeId, const char *context) { Oid inputTypeId = exprType(node); if (inputTypeId == targetTypeId) return node; /* no work */ if (can_coerce_type(1, &inputTypeId, &targetTypeId, COERCION_IMPLICIT)) - node = coerce_type(node, inputTypeId, targetTypeId, + node = coerce_type(pstate, node, inputTypeId, targetTypeId, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else elog(ERROR, "%s unable to convert to type %s", diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 10702e9a269..429a9ac8c8a 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.147 2003/04/08 23:20:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.148 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -39,7 +39,8 @@ static int expr_depth_counter = 0; bool Transform_null_equals = false; -static Node *typecast_expression(Node *expr, TypeName *typename); +static Node *typecast_expression(ParseState *pstate, Node *expr, + TypeName *typename); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformIndirection(ParseState *pstate, Node *basenode, List *indirection); @@ -112,17 +113,54 @@ transformExpr(ParseState *pstate, Node *expr) { ParamRef *pref = (ParamRef *) expr; int paramno = pref->number; - Oid paramtyp = param_type(paramno); + ParseState *toppstate; Param *param; List *fields; - if (!OidIsValid(paramtyp)) - elog(ERROR, "Parameter '$%d' is out of range", paramno); + /* + * Find topmost ParseState, which is where paramtype info + * lives. + */ + toppstate = pstate; + while (toppstate->parentParseState != NULL) + toppstate = toppstate->parentParseState; + + /* Check parameter number is in range */ + if (paramno <= 0) /* probably can't happen? */ + elog(ERROR, "Parameter '$%d' is out of range", + paramno); + if (paramno > toppstate->p_numparams) + { + if (!toppstate->p_variableparams) + elog(ERROR, "Parameter '$%d' is out of range", + paramno); + /* Okay to enlarge param array */ + if (toppstate->p_paramtypes) + toppstate->p_paramtypes = + (Oid *) repalloc(toppstate->p_paramtypes, + paramno * sizeof(Oid)); + else + toppstate->p_paramtypes = + (Oid *) palloc(paramno * sizeof(Oid)); + /* Zero out the previously-unreferenced slots */ + MemSet(toppstate->p_paramtypes + toppstate->p_numparams, + 0, + (paramno - toppstate->p_numparams) * sizeof(Oid)); + toppstate->p_numparams = paramno; + } + if (toppstate->p_variableparams) + { + /* If not seen before, initialize to UNKNOWN type */ + if (toppstate->p_paramtypes[paramno-1] == InvalidOid) + toppstate->p_paramtypes[paramno-1] = UNKNOWNOID; + } + param = makeNode(Param); param->paramkind = PARAM_NUM; param->paramid = (AttrNumber) paramno; - param->paramtype = paramtyp; + param->paramtype = toppstate->p_paramtypes[paramno-1]; result = (Node *) param; + /* handle qualification, if any */ foreach(fields, pref->fields) { @@ -143,7 +181,8 @@ transformExpr(ParseState *pstate, Node *expr) result = (Node *) make_const(val); if (con->typename != NULL) - result = typecast_expression(result, con->typename); + result = typecast_expression(pstate, result, + con->typename); break; } case T_ExprFieldSelect: @@ -170,7 +209,7 @@ transformExpr(ParseState *pstate, Node *expr) TypeCast *tc = (TypeCast *) expr; Node *arg = transformExpr(pstate, tc->arg); - result = typecast_expression(arg, tc->typename); + result = typecast_expression(pstate, arg, tc->typename); break; } case T_A_Expr: @@ -212,7 +251,8 @@ transformExpr(ParseState *pstate, Node *expr) Node *rexpr = transformExpr(pstate, a->rexpr); - result = (Node *) make_op(a->name, + result = (Node *) make_op(pstate, + a->name, lexpr, rexpr); } @@ -225,8 +265,8 @@ transformExpr(ParseState *pstate, Node *expr) Node *rexpr = transformExpr(pstate, a->rexpr); - lexpr = coerce_to_boolean(lexpr, "AND"); - rexpr = coerce_to_boolean(rexpr, "AND"); + lexpr = coerce_to_boolean(pstate, lexpr, "AND"); + rexpr = coerce_to_boolean(pstate, rexpr, "AND"); result = (Node *) makeBoolExpr(AND_EXPR, makeList2(lexpr, @@ -240,8 +280,8 @@ transformExpr(ParseState *pstate, Node *expr) Node *rexpr = transformExpr(pstate, a->rexpr); - lexpr = coerce_to_boolean(lexpr, "OR"); - rexpr = coerce_to_boolean(rexpr, "OR"); + lexpr = coerce_to_boolean(pstate, lexpr, "OR"); + rexpr = coerce_to_boolean(pstate, rexpr, "OR"); result = (Node *) makeBoolExpr(OR_EXPR, makeList2(lexpr, @@ -253,7 +293,7 @@ transformExpr(ParseState *pstate, Node *expr) Node *rexpr = transformExpr(pstate, a->rexpr); - rexpr = coerce_to_boolean(rexpr, "NOT"); + rexpr = coerce_to_boolean(pstate, rexpr, "NOT"); result = (Node *) makeBoolExpr(NOT_EXPR, makeList1(rexpr)); @@ -266,7 +306,8 @@ transformExpr(ParseState *pstate, Node *expr) Node *rexpr = transformExpr(pstate, a->rexpr); - result = (Node *) make_op(a->name, + result = (Node *) make_op(pstate, + a->name, lexpr, rexpr); if (((OpExpr *) result)->opresulttype != BOOLOID) @@ -284,7 +325,8 @@ transformExpr(ParseState *pstate, Node *expr) Node *rexpr = transformExpr(pstate, a->rexpr); - result = (Node *) make_op(a->name, + result = (Node *) make_op(pstate, + a->name, lexpr, rexpr); if (((OpExpr *) result)->opresulttype != BOOLOID) @@ -375,7 +417,7 @@ transformExpr(ParseState *pstate, Node *expr) break; } pstate->p_hasSubLinks = true; - qtrees = parse_analyze(sublink->subselect, pstate); + qtrees = parse_sub_analyze(sublink->subselect, pstate); if (length(qtrees) != 1) elog(ERROR, "Bad query in subselect"); qtree = (Query *) lfirst(qtrees); @@ -523,7 +565,7 @@ transformExpr(ParseState *pstate, Node *expr) if (needNot) { - expr = coerce_to_boolean(expr, "NOT"); + expr = coerce_to_boolean(pstate, expr, "NOT"); expr = (Node *) makeBoolExpr(NOT_EXPR, makeList1(expr)); } @@ -561,7 +603,8 @@ transformExpr(ParseState *pstate, Node *expr) } neww->expr = (Expr *) transformExpr(pstate, warg); - neww->expr = (Expr *) coerce_to_boolean((Node *) neww->expr, + neww->expr = (Expr *) coerce_to_boolean(pstate, + (Node *) neww->expr, "CASE/WHEN"); /* @@ -615,7 +658,8 @@ transformExpr(ParseState *pstate, Node *expr) /* Convert default result clause, if necessary */ newc->defresult = (Expr *) - coerce_to_common_type((Node *) newc->defresult, + coerce_to_common_type(pstate, + (Node *) newc->defresult, ptype, "CASE/ELSE"); @@ -625,7 +669,8 @@ transformExpr(ParseState *pstate, Node *expr) CaseWhen *w = (CaseWhen *) lfirst(args); w->result = (Expr *) - coerce_to_common_type((Node *) w->result, + coerce_to_common_type(pstate, + (Node *) w->result, ptype, "CASE/WHEN"); } @@ -666,7 +711,9 @@ transformExpr(ParseState *pstate, Node *expr) Node *e = (Node *) lfirst(element); Node *newe; - newe = coerce_to_common_type(e, element_type, "ARRAY"); + newe = coerce_to_common_type(pstate, e, + element_type, + "ARRAY"); newcoercedelems = lappend(newcoercedelems, newe); } @@ -753,7 +800,8 @@ transformExpr(ParseState *pstate, Node *expr) Node *e = (Node *) lfirst(args); Node *newe; - newe = coerce_to_common_type(e, newc->coalescetype, + newe = coerce_to_common_type(pstate, e, + newc->coalescetype, "COALESCE"); newcoercedargs = lappend(newcoercedargs, newe); } @@ -806,7 +854,9 @@ transformExpr(ParseState *pstate, Node *expr) b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg); - b->arg = (Expr *) coerce_to_boolean((Node *) b->arg, clausename); + b->arg = (Expr *) coerce_to_boolean(pstate, + (Node *) b->arg, + clausename); result = expr; break; @@ -1404,7 +1454,7 @@ exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) * the type name and then apply any necessary coercion function(s). */ static Node * -typecast_expression(Node *expr, TypeName *typename) +typecast_expression(ParseState *pstate, Node *expr, TypeName *typename) { Oid inputType = exprType(expr); Oid targetType; @@ -1414,7 +1464,7 @@ typecast_expression(Node *expr, TypeName *typename) if (inputType == InvalidOid) return expr; /* do nothing if NULL input */ - expr = coerce_to_target_type(expr, inputType, + expr = coerce_to_target_type(pstate, expr, inputType, targetType, typename->typmod, COERCION_EXPLICIT, COERCE_EXPLICIT_CAST); diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 7cbef965369..058b9aad73d 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.146 2003/04/24 21:16:43 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.147 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -253,7 +253,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * We can do it as a trivial coercion. coerce_type can handle * these cases, so why duplicate code... */ - return coerce_type(lfirst(fargs), actual_arg_types[0], rettype, + return coerce_type(pstate, lfirst(fargs), actual_arg_types[0], + rettype, COERCION_EXPLICIT, COERCE_EXPLICIT_CALL); } else if (fdresult == FUNCDETAIL_NORMAL) @@ -316,7 +317,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, rettype); /* perform the necessary typecasting of arguments */ - make_fn_arguments(fargs, actual_arg_types, declared_arg_types); + make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types); /* build the appropriate output structure */ if (fdresult == FUNCDETAIL_NORMAL) @@ -1145,9 +1146,13 @@ typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId) * allowed. * * Caution: given argument list is modified in-place. + * + * As with coerce_type, pstate may be NULL if no special unknown-Param + * processing is wanted. */ void -make_fn_arguments(List *fargs, +make_fn_arguments(ParseState *pstate, + List *fargs, Oid *actual_arg_types, Oid *declared_arg_types) { @@ -1159,7 +1164,8 @@ make_fn_arguments(List *fargs, /* types don't match? then force coercion using a function call... */ if (actual_arg_types[i] != declared_arg_types[i]) { - lfirst(current_fargs) = coerce_type(lfirst(current_fargs), + lfirst(current_fargs) = coerce_type(pstate, + lfirst(current_fargs), actual_arg_types[i], declared_arg_types[i], COERCION_IMPLICIT, diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 700c4b158de..2f5775212cb 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.77 2003/04/08 23:20:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.78 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -39,7 +39,12 @@ make_parsestate(ParseState *parentParseState) pstate = palloc0(sizeof(ParseState)); pstate->parentParseState = parentParseState; - pstate->p_last_resno = 1; + + /* Fill in fields that don't start at null/false/zero */ + pstate->p_next_resno = 1; + + if (parentParseState) + pstate->p_variableparams = parentParseState->p_variableparams; return pstate; } @@ -166,7 +171,8 @@ transformArraySubscripts(ParseState *pstate, { subexpr = transformExpr(pstate, ai->lidx); /* If it's not int4 already, try to coerce */ - subexpr = coerce_to_target_type(subexpr, exprType(subexpr), + subexpr = coerce_to_target_type(pstate, + subexpr, exprType(subexpr), INT4OID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); @@ -186,7 +192,8 @@ transformArraySubscripts(ParseState *pstate, } subexpr = transformExpr(pstate, ai->uidx); /* If it's not int4 already, try to coerce */ - subexpr = coerce_to_target_type(subexpr, exprType(subexpr), + subexpr = coerce_to_target_type(pstate, + subexpr, exprType(subexpr), INT4OID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); @@ -205,7 +212,8 @@ transformArraySubscripts(ParseState *pstate, if (typesource != InvalidOid) { - assignFrom = coerce_to_target_type(assignFrom, typesource, + assignFrom = coerce_to_target_type(pstate, + assignFrom, typesource, typeneeded, arrayTypMod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 3be29e64def..6238258ed2f 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.62 2003/04/08 23:20:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.63 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1019,9 +1019,12 @@ unary_op_error(List *op, Oid arg, bool is_left_op) * * Transform operator expression ensuring type compatibility. * This is where some type conversion happens. + * + * As with coerce_type, pstate may be NULL if no special unknown-Param + * processing is wanted. */ Expr * -make_op(List *opname, Node *ltree, Node *rtree) +make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree) { Oid ltypeId, rtypeId; @@ -1052,7 +1055,7 @@ make_op(List *opname, Node *ltree, Node *rtree) } /* Do typecasting and build the expression tree */ - result = make_op_expr(tup, ltree, rtree, ltypeId, rtypeId); + result = make_op_expr(pstate, tup, ltree, rtree, ltypeId, rtypeId); ReleaseSysCache(tup); @@ -1063,9 +1066,13 @@ make_op(List *opname, Node *ltree, Node *rtree) /* * make_op_expr() * Build operator expression using an already-looked-up operator. + * + * As with coerce_type, pstate may be NULL if no special unknown-Param + * processing is wanted. */ Expr * -make_op_expr(Operator op, Node *ltree, Node *rtree, +make_op_expr(ParseState *pstate, Operator op, + Node *ltree, Node *rtree, Oid ltypeId, Oid rtypeId) { Form_pg_operator opform = (Form_pg_operator) GETSTRUCT(op); @@ -1114,7 +1121,7 @@ make_op_expr(Operator op, Node *ltree, Node *rtree, opform->oprresult); /* perform the necessary typecasting of arguments */ - make_fn_arguments(args, actual_arg_types, declared_arg_types); + make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); /* and build the expression node */ result = makeNode(OpExpr); diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 4ae3bf23153..41fd98fc071 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.80 2002/12/12 15:49:39 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.81 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1447,7 +1447,7 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte) Node *varnode = (Node *) lfirst(vars); TargetEntry *te = makeNode(TargetEntry); - te->resdom = makeResdom((AttrNumber) (pstate->p_last_resno)++, + te->resdom = makeResdom((AttrNumber) pstate->p_next_resno++, exprType(varnode), exprTypmod(varnode), label, diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index e1be47b3ec5..dc8f241d45e 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.99 2003/04/08 23:20:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.100 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -73,7 +73,7 @@ transformTargetEntry(ParseState *pstate, colname = FigureColname(node); } - resnode = makeResdom((AttrNumber) pstate->p_last_resno++, + resnode = makeResdom((AttrNumber) pstate->p_next_resno++, type_id, type_mod, colname, @@ -290,7 +290,8 @@ updateTargetListEntry(ParseState *pstate, if (type_id != InvalidOid) { tle->expr = (Expr *) - coerce_to_target_type((Node *) tle->expr, type_id, + coerce_to_target_type(pstate, + (Node *) tle->expr, type_id, attrtype, attrtypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 85aeafacc59..d1a324dbf0a 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.56 2003/04/27 20:09:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.57 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -437,7 +437,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod) initStringInfo(&buf); appendStringInfo(&buf, "SELECT (NULL::%s)", str); - raw_parsetree_list = parser(buf.data, NULL, 0); + raw_parsetree_list = raw_parser(buf.data); /* * Make sure we got back exactly what we expected and no more; diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c index 16745f7b370..37436d30079 100644 --- a/src/backend/parser/parser.c +++ b/src/backend/parser/parser.c @@ -14,7 +14,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.56 2003/04/27 20:09:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.57 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,32 +30,27 @@ List *parsetree; /* result of parsing is left here */ -static Oid *param_type_info; /* state for param_type() */ -static int param_count; - static int lookahead_token; /* one-token lookahead */ static bool have_lookahead; /* lookahead_token set? */ /* - * parser - * Given a query in string form, and optionally info about - * parameter types, do lexical and syntactic analysis. + * raw_parser + * Given a query in string form, do lexical and grammatical analysis. * * Returns a list of raw (un-analyzed) parse trees. */ List * -parser(const char *str, Oid *typev, int nargs) +raw_parser(const char *str) { int yyresult; - parsetree = NIL; /* in case parser forgets to set it */ + parsetree = NIL; /* in case grammar forgets to set it */ have_lookahead = false; scanner_init(str); parser_init(); parse_expr_init(); - parser_param_set(typev, nargs); yyresult = yyparse(); @@ -70,35 +65,6 @@ parser(const char *str, Oid *typev, int nargs) /* - * Save information needed to fill out the type of Param references ($n) - * - * This is used for SQL functions, PREPARE statements, etc. It's split - * out from parser() setup because PREPARE needs to change the info after - * the grammar runs and before parse analysis is done on the preparable - * query. - */ -void -parser_param_set(Oid *typev, int nargs) -{ - param_type_info = typev; - param_count = nargs; -} - -/* - * param_type() - * - * Fetch a parameter type previously passed to parser_param_set - */ -Oid -param_type(int t) -{ - if (t > param_count || t <= 0) - return InvalidOid; - return param_type_info[t - 1]; -} - - -/* * Intermediate filter between parser and base lexer (base_yylex in scan.l). * * The filter is needed because in some cases SQL92 requires more than one diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index e39ee0efbe7..8ca7fb954f0 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.118 2003/02/25 23:47:43 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.119 2003/04/29 22:13:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -498,7 +498,8 @@ build_column_default(Relation rel, int attrno) */ exprtype = exprType(expr); - expr = coerce_to_target_type(expr, exprtype, + expr = coerce_to_target_type(NULL, /* no UNKNOWN params here */ + expr, exprtype, atttype, atttypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index dca6455b0d0..bc98d1d91ec 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.325 2003/04/27 20:09:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.326 2003/04/29 22:13:11 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -339,8 +339,8 @@ ReadCommand(StringInfo inBuf) */ List * pg_parse_and_rewrite(const char *query_string, /* string to execute */ - Oid *typev, /* parameter types */ - int nargs) /* number of parameters */ + Oid *paramTypes, /* parameter types */ + int numParams) /* number of parameters */ { List *raw_parsetree_list; List *querytree_list; @@ -349,7 +349,7 @@ pg_parse_and_rewrite(const char *query_string, /* string to execute */ /* * (1) parse the request string into a list of raw parse trees. */ - raw_parsetree_list = pg_parse_query(query_string, typev, nargs); + raw_parsetree_list = pg_parse_query(query_string); /* * (2) Do parse analysis and rule rewrite. @@ -360,7 +360,9 @@ pg_parse_and_rewrite(const char *query_string, /* string to execute */ Node *parsetree = (Node *) lfirst(list_item); querytree_list = nconc(querytree_list, - pg_analyze_and_rewrite(parsetree)); + pg_analyze_and_rewrite(parsetree, + paramTypes, + numParams)); } return querytree_list; @@ -380,7 +382,7 @@ pg_parse_and_rewrite(const char *query_string, /* string to execute */ * commands are not processed any further than the raw parse stage. */ List * -pg_parse_query(const char *query_string, Oid *typev, int nargs) +pg_parse_query(const char *query_string) { List *raw_parsetree_list; @@ -390,7 +392,7 @@ pg_parse_query(const char *query_string, Oid *typev, int nargs) if (log_parser_stats) ResetUsage(); - raw_parsetree_list = parser(query_string, typev, nargs); + raw_parsetree_list = raw_parser(query_string); if (log_parser_stats) ShowUsage("PARSER STATISTICS"); @@ -399,8 +401,8 @@ pg_parse_query(const char *query_string, Oid *typev, int nargs) } /* - * Given a raw parsetree (gram.y output), perform parse analysis and - * rule rewriting. + * Given a raw parsetree (gram.y output), and optionally information about + * types of parameter symbols ($n), perform parse analysis and rule rewriting. * * A list of Query nodes is returned, since either the analyzer or the * rewriter might expand one query to several. @@ -408,7 +410,7 @@ pg_parse_query(const char *query_string, Oid *typev, int nargs) * NOTE: for reasons mentioned above, this must be separate from raw parsing. */ List * -pg_analyze_and_rewrite(Node *parsetree) +pg_analyze_and_rewrite(Node *parsetree, Oid *paramTypes, int numParams) { List *querytree_list; List *list_item; @@ -421,7 +423,7 @@ pg_analyze_and_rewrite(Node *parsetree) if (log_parser_stats) ResetUsage(); - querytree_list = parse_analyze(parsetree, NULL); + querytree_list = parse_analyze(parsetree, paramTypes, numParams); if (log_parser_stats) { @@ -562,8 +564,7 @@ pg_plan_query(Query *querytree) * * ---------------------------------------------------------------- */ - -void +static void pg_exec_query_string(const char *query_string, /* string to execute */ CommandDest dest, /* where results should go */ MemoryContext parse_context) /* context for @@ -614,7 +615,7 @@ pg_exec_query_string(const char *query_string, /* string to execute */ * Do basic parsing of the query or queries (this should be safe even * if we are in aborted transaction state!) */ - parsetree_list = pg_parse_query(query_string, NULL, 0); + parsetree_list = pg_parse_query(query_string); /* * Switch back to execution context to enter the loop. @@ -710,7 +711,7 @@ pg_exec_query_string(const char *query_string, /* string to execute */ */ oldcontext = MemoryContextSwitchTo(parse_context); - querytree_list = pg_analyze_and_rewrite(parsetree); + querytree_list = pg_analyze_and_rewrite(parsetree, NULL, 0); /* * Switch back to execution context for planning and execution. @@ -1826,7 +1827,7 @@ PostgresMain(int argc, char *argv[], const char *username) if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.325 $ $Date: 2003/04/27 20:09:44 $\n"); + puts("$Revision: 1.326 $ $Date: 2003/04/29 22:13:11 $\n"); } /* diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h index f780ec4d294..b7d6a7d6658 100644 --- a/src/include/parser/analyze.h +++ b/src/include/parser/analyze.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.h,v 1.20 2002/06/20 20:29:51 momjian Exp $ + * $Id: analyze.h,v 1.21 2003/04/29 22:13:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,7 +15,11 @@ #include "parser/parse_node.h" -extern List *parse_analyze(Node *parseTree, ParseState *parentParseState); + +extern List *parse_analyze(Node *parseTree, Oid *paramTypes, int numParams); +extern List *parse_analyze_varparams(Node *parseTree, Oid **paramTypes, + int *numParams); +extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState); extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt); extern void CheckSelectForUpdate(Query *qry); diff --git a/src/include/parser/gramparse.h b/src/include/parser/gramparse.h index 97108f2b1c9..f9a03959116 100644 --- a/src/include/parser/gramparse.h +++ b/src/include/parser/gramparse.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: gramparse.h,v 1.26 2003/04/27 20:09:44 tgl Exp $ + * $Id: gramparse.h,v 1.27 2003/04/29 22:13:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,9 +17,8 @@ #include "nodes/parsenodes.h" + /* from parser.c */ -extern void parser_param_set(Oid *typev, int nargs); -extern Oid param_type(int t); extern int yylex(void); /* from scan.l */ diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index b21a83601bf..de2ea95db85 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_coerce.h,v 1.50 2003/04/08 23:20:04 tgl Exp $ + * $Id: parse_coerce.h,v 1.51 2003/04/29 22:13:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "catalog/pg_type.h" #include "parser/parse_node.h" + typedef enum CATEGORY { INVALID_TYPE, @@ -38,22 +39,26 @@ extern bool IsBinaryCoercible(Oid srctype, Oid targettype); extern bool IsPreferredType(CATEGORY category, Oid type); extern CATEGORY TypeCategory(Oid type); -extern Node *coerce_to_target_type(Node *expr, Oid exprtype, +extern Node *coerce_to_target_type(ParseState *pstate, + Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat); extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, CoercionContext ccontext); -extern Node *coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId, +extern Node *coerce_type(ParseState *pstate, Node *node, + Oid inputTypeId, Oid targetTypeId, CoercionContext ccontext, CoercionForm cformat); extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat); -extern Node *coerce_to_boolean(Node *node, const char *constructName); +extern Node *coerce_to_boolean(ParseState *pstate, Node *node, + const char *constructName); extern Oid select_common_type(List *typeids, const char *context); -extern Node *coerce_to_common_type(Node *node, Oid targetTypeId, - const char *context); +extern Node *coerce_to_common_type(ParseState *pstate, Node *node, + Oid targetTypeId, + const char *context); extern bool check_generic_type_consistency(Oid *actual_arg_types, Oid *declared_arg_types, diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h index 3bd369e3c70..2b1a1fad4b9 100644 --- a/src/include/parser/parse_func.h +++ b/src/include/parser/parse_func.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_func.h,v 1.44 2003/04/08 23:20:04 tgl Exp $ + * $Id: parse_func.h,v 1.45 2003/04/29 22:13:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,7 @@ #include "parser/parse_node.h" + /* * This structure is used to explore the inheritance hierarchy above * nodes in the type tree in order to disambiguate among polymorphic @@ -49,7 +50,8 @@ extern FuncDetailCode func_get_detail(List *funcname, List *fargs, extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId); -extern void make_fn_arguments(List *fargs, +extern void make_fn_arguments(ParseState *pstate, + List *fargs, Oid *actual_arg_types, Oid *declared_arg_types); diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 12dcaccbefd..1f52963e5c0 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -1,12 +1,13 @@ /*------------------------------------------------------------------------- * * parse_node.h + * Internal definitions for parser * * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_node.h,v 1.34 2003/04/08 23:20:04 tgl Exp $ + * $Id: parse_node.h,v 1.35 2003/04/29 22:13:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,6 +33,15 @@ * joinlist. Note that an RTE that is present in p_namespace, but does not * have its inFromCl flag set, is accessible only with an explicit qualifier; * lookups of unqualified column names should ignore it. + * + * p_paramtypes: an array of p_numparams type OIDs for $n parameter symbols + * (zeroth entry in array corresponds to $1). If p_variableparams is true, the + * set of param types is not predetermined; in that case, a zero array entry + * means that parameter number hasn't been seen, and UNKNOWNOID means the + * parameter has been used but its type is not yet known. NOTE: in a stack + * of ParseStates, only the topmost ParseState contains paramtype info; but + * we copy the p_variableparams flag down to the child nodes for speed in + * coerce_type. */ typedef struct ParseState { @@ -40,9 +50,12 @@ typedef struct ParseState List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ List *p_namespace; /* current lookup namespace (join items) */ - int p_last_resno; /* last targetlist resno assigned */ + Oid *p_paramtypes; /* OIDs of types for $n parameter symbols */ + int p_numparams; /* allocated size of p_paramtypes[] */ + int p_next_resno; /* next targetlist resno to assign */ List *p_forUpdate; /* FOR UPDATE clause, if any (see gram.y) */ Node *p_value_substitute; /* what to replace VALUE with, if any */ + bool p_variableparams; bool p_hasAggs; bool p_hasSubLinks; bool p_is_insert; diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h index 2cde7189dda..6d2268bf557 100644 --- a/src/include/parser/parse_oper.h +++ b/src/include/parser/parse_oper.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_oper.h,v 1.24 2003/04/08 23:20:04 tgl Exp $ + * $Id: parse_oper.h,v 1.25 2003/04/29 22:13:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,7 +15,8 @@ #define PARSE_OPER_H #include "access/htup.h" -#include "nodes/parsenodes.h" +#include "parser/parse_node.h" + typedef HeapTuple Operator; @@ -50,8 +51,10 @@ extern Oid oprid(Operator op); extern Oid oprfuncid(Operator op); /* Build expression tree for an operator invocation */ -extern Expr *make_op(List *opname, Node *ltree, Node *rtree); -extern Expr *make_op_expr(Operator op, Node *ltree, Node *rtree, +extern Expr *make_op(ParseState *pstate, List *opname, + Node *ltree, Node *rtree); +extern Expr *make_op_expr(ParseState *pstate, Operator op, + Node *ltree, Node *rtree, Oid ltypeId, Oid rtypeId); #endif /* PARSE_OPER_H */ diff --git a/src/include/parser/parser.h b/src/include/parser/parser.h index 42d82869f08..e5e5bddfafc 100644 --- a/src/include/parser/parser.h +++ b/src/include/parser/parser.h @@ -1,21 +1,19 @@ /*------------------------------------------------------------------------- * * parser.h - * + * Definitions for the "raw" parser (lex and yacc phases only) * * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parser.h,v 1.14 2003/04/27 20:09:44 tgl Exp $ + * $Id: parser.h,v 1.15 2003/04/29 22:13:11 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef PARSER_H #define PARSER_H -#include "parser/parse_node.h" - -extern List *parser(const char *str, Oid *typev, int nargs); +extern List *raw_parser(const char *str); #endif /* PARSER_H */ diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index 4235d6d257f..b81df05320c 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: tcopprot.h,v 1.54 2003/04/27 20:09:44 tgl Exp $ + * $Id: tcopprot.h,v 1.55 2003/04/29 22:13:11 tgl Exp $ * * OLD COMMENTS * This file was created so that other c files could get the two @@ -35,14 +35,12 @@ extern DLLIMPORT const char *debug_query_string; #ifndef BOOTSTRAP_INCLUDE -extern List *pg_parse_query(const char *query_string, Oid *typev, int nargs); -extern List *pg_analyze_and_rewrite(Node *parsetree); +extern List *pg_parse_query(const char *query_string); +extern List *pg_analyze_and_rewrite(Node *parsetree, + Oid *paramTypes, int numParams); extern List *pg_parse_and_rewrite(const char *query_string, - Oid *typev, int nargs); + Oid *paramTypes, int numParams); extern Plan *pg_plan_query(Query *querytree); -extern void pg_exec_query_string(const char *query_string, - CommandDest dest, - MemoryContext parse_context); #endif /* BOOTSTRAP_INCLUDE */ |