diff options
Diffstat (limited to 'src/backend/parser/analyze.c')
| -rw-r--r-- | src/backend/parser/analyze.c | 139 |
1 files changed, 106 insertions, 33 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index b187b03666..3329e9c964 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -10,8 +10,8 @@ * utility commands, no locks are obtained here (and if they were, we could * not be sure we'd still have them at execution). Hence the general rule * for utility commands is to just dump them into a Query node untransformed. - * DECLARE CURSOR and EXPLAIN are exceptions because they contain - * optimizable statements. + * DECLARE CURSOR, EXPLAIN, and CREATE TABLE AS are exceptions because they + * contain optimizable statements, which we should transform. * * * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group @@ -62,6 +62,8 @@ static Query *transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt); static Query *transformExplainStmt(ParseState *pstate, ExplainStmt *stmt); +static Query *transformCreateTableAsStmt(ParseState *pstate, + CreateTableAsStmt *stmt); static void transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, bool pushedDown); @@ -91,7 +93,7 @@ parse_analyze(Node *parseTree, const char *sourceText, if (numParams > 0) parse_fixed_parameters(pstate, paramTypes, numParams); - query = transformStmt(pstate, parseTree); + query = transformTopLevelStmt(pstate, parseTree); free_parsestate(pstate); @@ -118,7 +120,7 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText, parse_variable_parameters(pstate, paramTypes, numParams); - query = transformStmt(pstate, parseTree); + query = transformTopLevelStmt(pstate, parseTree); /* make sure all is well with parameter types */ check_variable_parameters(pstate, query); @@ -151,8 +153,52 @@ parse_sub_analyze(Node *parseTree, ParseState *parentParseState, } /* - * transformStmt - + * transformTopLevelStmt - * transform a Parse tree into a Query tree. + * + * The only thing we do here that we don't do in transformStmt() is to + * convert SELECT ... INTO into CREATE TABLE AS. Since utility statements + * aren't allowed within larger statements, this is only allowed at the top + * of the parse tree, and so we only try it before entering the recursive + * transformStmt() processing. + */ +Query * +transformTopLevelStmt(ParseState *pstate, Node *parseTree) +{ + if (IsA(parseTree, SelectStmt)) + { + SelectStmt *stmt = (SelectStmt *) parseTree; + + /* If it's a set-operation tree, drill down to leftmost SelectStmt */ + while (stmt && stmt->op != SETOP_NONE) + stmt = stmt->larg; + Assert(stmt && IsA(stmt, SelectStmt) && stmt->larg == NULL); + + if (stmt->intoClause) + { + CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); + + ctas->query = parseTree; + ctas->into = stmt->intoClause; + ctas->is_select_into = true; + + /* + * Remove the intoClause from the SelectStmt. This makes it safe + * for transformSelectStmt to complain if it finds intoClause set + * (implying that the INTO appeared in a disallowed place). + */ + stmt->intoClause = NULL; + + parseTree = (Node *) ctas; + } + } + + return transformStmt(pstate, parseTree); +} + +/* + * transformStmt - + * recursively transform a Parse tree into a Query tree. */ Query * transformStmt(ParseState *pstate, Node *parseTree) @@ -202,6 +248,11 @@ transformStmt(ParseState *pstate, Node *parseTree) (ExplainStmt *) parseTree); break; + case T_CreateTableAsStmt: + result = transformCreateTableAsStmt(pstate, + (CreateTableAsStmt *) parseTree); + break; + default: /* @@ -258,6 +309,7 @@ analyze_requires_snapshot(Node *parseTree) break; case T_ExplainStmt: + case T_CreateTableAsStmt: /* yes, because we must analyze the contained statement */ result = true; break; @@ -459,17 +511,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) free_parsestate(sub_pstate); - /* The grammar should have produced a SELECT, but it might have INTO */ + /* The grammar should have produced a SELECT */ if (!IsA(selectQuery, Query) || selectQuery->commandType != CMD_SELECT || selectQuery->utilityStmt != NULL) elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT"); - if (selectQuery->intoClause) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("INSERT ... SELECT cannot specify INTO"), - parser_errposition(pstate, - exprLocation((Node *) selectQuery->intoClause)))); /* * Make the source be a subquery in the INSERT's rangetable, and add @@ -539,6 +585,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) int sublist_length = -1; int i; + Assert(selectStmt->intoClause == NULL); + foreach(lc, selectStmt->valuesLists) { List *sublist = (List *) lfirst(lc); @@ -653,6 +701,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *valuesLists = selectStmt->valuesLists; Assert(list_length(valuesLists) == 1); + Assert(selectStmt->intoClause == NULL); /* Do basic expression transformation (same as a ROW() expr) */ exprList = transformExpressionList(pstate, @@ -886,6 +935,14 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } + /* Complain if we get called from someplace where INTO is not allowed */ + if (stmt->intoClause) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("SELECT ... INTO is not allowed here"), + parser_errposition(pstate, + exprLocation((Node *) stmt->intoClause)))); + /* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */ pstate->p_locking_clause = stmt->lockingClause; @@ -963,9 +1020,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) pstate->p_windowdefs, &qry->targetList); - /* SELECT INTO/CREATE TABLE AS spec is just passed through */ - qry->intoClause = stmt->intoClause; - qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); @@ -1013,6 +1067,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) /* Most SELECT stuff doesn't apply in a VALUES clause */ Assert(stmt->distinctClause == NIL); + Assert(stmt->intoClause == NULL); Assert(stmt->targetList == NIL); Assert(stmt->fromClause == NIL); Assert(stmt->whereClause == NULL); @@ -1185,9 +1240,6 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"))); - /* CREATE TABLE AS spec is just passed through */ - qry->intoClause = stmt->intoClause; - /* * There mustn't have been any table references in the expressions, else * strange things would happen, like Cartesian products of those tables @@ -1286,21 +1338,27 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) } /* - * Find leftmost leaf SelectStmt; extract the one-time-only items from it - * and from the top-level node. + * Find leftmost leaf SelectStmt. We currently only need to do this in + * order to deliver a suitable error message if there's an INTO clause + * there, implying the set-op tree is in a context that doesn't allow + * INTO. (transformSetOperationTree would throw error anyway, but it + * seems worth the trouble to throw a different error for non-leftmost + * INTO, so we produce that error in transformSetOperationTree.) */ leftmostSelect = stmt->larg; while (leftmostSelect && leftmostSelect->op != SETOP_NONE) leftmostSelect = leftmostSelect->larg; Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) && leftmostSelect->larg == NULL); - qry->intoClause = leftmostSelect->intoClause; - - /* clear this to prevent complaints in transformSetOperationTree() */ - leftmostSelect->intoClause = NULL; + if (leftmostSelect->intoClause) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("SELECT ... INTO is not allowed here"), + parser_errposition(pstate, + exprLocation((Node *) leftmostSelect->intoClause)))); /* - * These are not one-time, exactly, but we want to process them here and + * We need to extract ORDER BY and other top-level clauses here and * not let transformSetOperationTree() see them --- else it'll just * recurse right back here! */ @@ -2107,14 +2165,6 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) result->utilityStmt != NULL) elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR"); - /* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */ - if (result->intoClause) - ereport(ERROR, - (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), - errmsg("DECLARE CURSOR cannot specify INTO"), - parser_errposition(pstate, - exprLocation((Node *) result->intoClause)))); - /* * We also disallow data-modifying WITH in a cursor. (This could be * allowed, but the semantics of when the updates occur might be @@ -2170,6 +2220,29 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt) { Query *result; + /* transform contained query, allowing SELECT INTO */ + stmt->query = (Node *) transformTopLevelStmt(pstate, stmt->query); + + /* represent the command as a utility Query */ + result = makeNode(Query); + result->commandType = CMD_UTILITY; + result->utilityStmt = (Node *) stmt; + + return result; +} + + +/* + * transformCreateTableAsStmt - + * transform a CREATE TABLE AS (or SELECT ... INTO) Statement + * + * As with EXPLAIN, transform the contained statement now. + */ +static Query * +transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) +{ + Query *result; + /* transform contained query */ stmt->query = (Node *) transformStmt(pstate, stmt->query); |
