summaryrefslogtreecommitdiff
path: root/src/backend/parser/analyze.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r--src/backend/parser/analyze.c139
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);