diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 29 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 3 | ||||
-rw-r--r-- | src/backend/parser/parse_cte.c | 11 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 2 | ||||
-rw-r--r-- | src/backend/parser/parse_node.c | 1 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 33 |
6 files changed, 67 insertions, 12 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index a02a77a03a5..f954dc15f0e 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -156,13 +156,15 @@ parse_analyze_varparams(RawStmt *parseTree, const char *sourceText, Query * parse_sub_analyze(Node *parseTree, ParseState *parentParseState, CommonTableExpr *parentCTE, - bool locked_from_parent) + bool locked_from_parent, + bool resolve_unknowns) { ParseState *pstate = make_parsestate(parentParseState); Query *query; pstate->p_parent_cte = parentCTE; pstate->p_locked_from_parent = locked_from_parent; + pstate->p_resolve_unknowns = resolve_unknowns; query = transformStmt(pstate, parseTree); @@ -570,10 +572,17 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * otherwise the behavior of SELECT within INSERT might be different * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had * bugs of just that nature...) + * + * The sole exception is that we prevent resolving unknown-type + * outputs as TEXT. This does not change the semantics since if the + * column type matters semantically, it would have been resolved to + * something else anyway. Doing this lets us resolve such outputs as + * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; + sub_pstate->p_resolve_unknowns = false; selectQuery = transformStmt(sub_pstate, stmt->selectStmt); @@ -1269,6 +1278,10 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) pstate->p_windowdefs, &qry->targetList); + /* resolve any still-unresolved output columns as being type text */ + if (pstate->p_resolve_unknowns) + resolveTargetListUnknowns(pstate, qry->targetList); + qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); @@ -1843,11 +1856,19 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, /* * Transform SelectStmt into a Query. * + * This works the same as SELECT transformation normally would, except + * that we prevent resolving unknown-type outputs as TEXT. This does + * not change the subquery's semantics since if the column type + * matters semantically, it would have been resolved to something else + * anyway. Doing this lets us resolve such outputs using + * select_common_type(), below. + * * Note: previously transformed sub-queries don't affect the parsing * of this sub-query, because they are not in the toplevel pstate's * namespace list. */ - selectQuery = parse_sub_analyze((Node *) stmt, pstate, NULL, false); + selectQuery = parse_sub_analyze((Node *) stmt, pstate, + NULL, false, false); /* * Check for bogus references to Vars on the current query level (but @@ -2350,6 +2371,10 @@ transformReturningList(ParseState *pstate, List *returningList) /* mark column origins */ markTargetListOrigins(pstate, rlist); + /* resolve any still-unresolved output columns as being type text */ + if (pstate->p_resolve_unknowns) + resolveTargetListUnknowns(pstate, rlist); + /* restore state */ pstate->p_next_resno = save_next_resno; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 624ab41371f..4769e786202 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -471,7 +471,8 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) * Analyze and transform the subquery. */ query = parse_sub_analyze(r->subquery, pstate, NULL, - isLockedRefname(pstate, r->alias->aliasname)); + isLockedRefname(pstate, r->alias->aliasname), + true); /* Restore state */ pstate->p_lateral_active = false; diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c index fc8c15b2681..dfbcaa2cdcc 100644 --- a/src/backend/parser/parse_cte.c +++ b/src/backend/parser/parse_cte.c @@ -241,7 +241,7 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) /* Analysis not done already */ Assert(!IsA(cte->ctequery, Query)); - query = parse_sub_analyze(cte->ctequery, pstate, cte, false); + query = parse_sub_analyze(cte->ctequery, pstate, cte, false, true); cte->ctequery = (Node *) query; /* @@ -393,11 +393,10 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) /* * If the CTE is recursive, force the exposed column type of any - * "unknown" column to "text". This corresponds to the fact that - * SELECT 'foo' UNION SELECT 'bar' will ultimately produce text. We - * might see "unknown" as a result of an untyped literal in the - * non-recursive term's select list, and if we don't convert to text - * then we'll have a mismatch against the UNION result. + * "unknown" column to "text". We must deal with this here because + * we're called on the non-recursive term before there's been any + * attempt to force unknown output columns to some other type. We + * have to resolve unknowns before looking at the recursive term. * * The column might contain 'foo' COLLATE "bar", so don't override * collation if it's already set. diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index add3be65660..c43ef19df5c 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -1846,7 +1846,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) /* * OK, let's transform the sub-SELECT. */ - qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false); + qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false, true); /* * Check that we got a SELECT. Anything else should be impossible given diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 73e7d65c359..2a5f147ca1d 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -51,6 +51,7 @@ make_parsestate(ParseState *parentParseState) /* Fill in fields that don't start at null/false/zero */ pstate->p_next_resno = 1; + pstate->p_resolve_unknowns = true; if (parentParseState) { diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 081a8dd468b..2576e312390 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -289,12 +289,41 @@ transformExpressionList(ParseState *pstate, List *exprlist, /* + * resolveTargetListUnknowns() + * Convert any unknown-type targetlist entries to type TEXT. + * + * We do this after we've exhausted all other ways of identifying the output + * column types of a query. + */ +void +resolveTargetListUnknowns(ParseState *pstate, List *targetlist) +{ + ListCell *l; + + foreach(l, targetlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + Oid restype = exprType((Node *) tle->expr); + + if (restype == UNKNOWNOID) + { + tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, + restype, TEXTOID, -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + -1); + } + } +} + + +/* * markTargetListOrigins() * Mark targetlist columns that are simple Vars with the source * table's OID and column number. * - * Currently, this is done only for SELECT targetlists, since we only - * need the info if we are going to send it to the frontend. + * Currently, this is done only for SELECT targetlists and RETURNING lists, + * since we only need the info if we are going to send it to the frontend. */ void markTargetListOrigins(ParseState *pstate, List *targetlist) |