aboutsummaryrefslogtreecommitdiff
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.c178
1 files changed, 149 insertions, 29 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a4e4418b145..68475387be3 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -20,7 +20,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.362 2007/03/13 00:33:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.363 2007/04/27 22:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -118,6 +118,10 @@ static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
+static Query *transformDeclareCursorStmt(ParseState *pstate,
+ DeclareCursorStmt *stmt);
+static Query *transformExplainStmt(ParseState *pstate,
+ ExplainStmt *stmt);
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
List **extras_before, List **extras_after);
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
@@ -313,20 +317,6 @@ transformStmt(ParseState *pstate, Node *parseTree,
switch (nodeTag(parseTree))
{
/*
- * Non-optimizable statements
- */
- case T_CreateStmt:
- result = transformCreateStmt(pstate, (CreateStmt *) parseTree,
- extras_before, extras_after);
- break;
-
- case T_AlterTableStmt:
- result = transformAlterTableStmt(pstate,
- (AlterTableStmt *) parseTree,
- extras_before, extras_after);
- break;
-
- /*
* Optimizable statements
*/
case T_InsertStmt:
@@ -355,6 +345,33 @@ transformStmt(ParseState *pstate, Node *parseTree,
}
break;
+ /*
+ * Non-optimizable statements
+ */
+ case T_CreateStmt:
+ result = transformCreateStmt(pstate, (CreateStmt *) parseTree,
+ extras_before, extras_after);
+ break;
+
+ case T_AlterTableStmt:
+ result = transformAlterTableStmt(pstate,
+ (AlterTableStmt *) parseTree,
+ extras_before, extras_after);
+ break;
+
+ /*
+ * Special cases
+ */
+ case T_DeclareCursorStmt:
+ result = transformDeclareCursorStmt(pstate,
+ (DeclareCursorStmt *) parseTree);
+ break;
+
+ case T_ExplainStmt:
+ result = transformExplainStmt(pstate,
+ (ExplainStmt *) parseTree);
+ break;
+
default:
/*
@@ -546,9 +563,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
release_pstate_resources(sub_pstate);
pfree(sub_pstate);
+ /* The grammar should have produced a SELECT, but it might have INTO */
Assert(IsA(selectQuery, Query));
Assert(selectQuery->commandType == CMD_SELECT);
- if (selectQuery->into)
+ Assert(selectQuery->utilityStmt == NULL);
+ if (selectQuery->intoClause)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("INSERT ... SELECT cannot specify INTO")));
@@ -2029,6 +2048,8 @@ analyzeRuleStmt(RuleStmt *stmt, const char *queryString,
/*
* transformSelectStmt -
* transforms a Select Statement
+ *
+ * Note: this is also used for DECLARE CURSOR statements.
*/
static Query *
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
@@ -2085,11 +2106,11 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
"LIMIT");
/* handle any SELECT INTO/CREATE TABLE AS spec */
- if (stmt->into)
+ if (stmt->intoClause)
{
- qry->into = stmt->into;
- if (stmt->into->colNames)
- applyColumnNames(qry->targetList, stmt->into->colNames);
+ qry->intoClause = stmt->intoClause;
+ if (stmt->intoClause->colNames)
+ applyColumnNames(qry->targetList, stmt->intoClause->colNames);
}
qry->rtable = pstate->p_rtable;
@@ -2254,11 +2275,11 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
/* handle any CREATE TABLE AS spec */
- if (stmt->into)
+ if (stmt->intoClause)
{
- qry->into = stmt->into;
- if (stmt->into->colNames)
- applyColumnNames(qry->targetList, stmt->into->colNames);
+ qry->intoClause = stmt->intoClause;
+ if (stmt->intoClause->colNames)
+ applyColumnNames(qry->targetList, stmt->intoClause->colNames);
}
/*
@@ -2345,14 +2366,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
leftmostSelect = leftmostSelect->larg;
Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&
leftmostSelect->larg == NULL);
- if (leftmostSelect->into)
+ if (leftmostSelect->intoClause)
{
- qry->into = leftmostSelect->into;
- intoColNames = leftmostSelect->into->colNames;
+ qry->intoClause = leftmostSelect->intoClause;
+ intoColNames = leftmostSelect->intoClause->colNames;
}
/* clear this to prevent complaints in transformSetOperationTree() */
- leftmostSelect->into = NULL;
+ leftmostSelect->intoClause = NULL;
/*
* These are not one-time, exactly, but we want to process them here and
@@ -2533,7 +2554,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
/*
* Validity-check both leaf and internal SELECTs for disallowed ops.
*/
- if (stmt->into)
+ if (stmt->intoClause)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT")));
@@ -3113,6 +3134,105 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
}
+/*
+ * transformDeclareCursorStmt -
+ * transform a DECLARE CURSOR Statement
+ *
+ * DECLARE CURSOR is a hybrid case: it's an optimizable statement (in fact not
+ * significantly different from a SELECT) as far as parsing/rewriting/planning
+ * are concerned, but it's not passed to the executor and so in that sense is
+ * a utility statement. We transform it into a Query exactly as if it were
+ * a SELECT, then stick the original DeclareCursorStmt into the utilityStmt
+ * field to carry the cursor name and options.
+ */
+static Query *
+transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
+{
+ Query *result;
+ List *extras_before = NIL,
+ *extras_after = NIL;
+
+ /*
+ * Don't allow both SCROLL and NO SCROLL to be specified
+ */
+ if ((stmt->options & CURSOR_OPT_SCROLL) &&
+ (stmt->options & CURSOR_OPT_NO_SCROLL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+ errmsg("cannot specify both SCROLL and NO SCROLL")));
+
+ result = transformStmt(pstate, stmt->query,
+ &extras_before, &extras_after);
+
+ /* Shouldn't get any extras, since grammar only allows SelectStmt */
+ if (extras_before || extras_after)
+ elog(ERROR, "unexpected extra stuff in cursor statement");
+ if (!IsA(result, Query) ||
+ result->commandType != CMD_SELECT ||
+ result->utilityStmt != NULL)
+ elog(ERROR, "unexpected non-SELECT command in cursor statement");
+
+ /* 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")));
+
+ /* Implementation restriction (might go away someday) */
+ if (result->rowMarks != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
+ errdetail("Cursors must be READ ONLY.")));
+
+ /* We won't need the raw querytree any more */
+ stmt->query = NULL;
+
+ result->utilityStmt = (Node *) stmt;
+
+ return result;
+}
+
+
+/*
+ * transformExplainStmt -
+ * transform an EXPLAIN Statement
+ *
+ * EXPLAIN is just like other utility statements in that we emit it as a
+ * CMD_UTILITY Query node with no transformation of the raw parse tree.
+ * However, if p_variableparams is set, it could be that the client is
+ * expecting us to resolve parameter types in something like
+ * EXPLAIN SELECT * FROM tab WHERE col = $1
+ * To deal with such cases, we run parse analysis and throw away the result;
+ * this is a bit grotty but not worth contorting the rest of the system for.
+ * (The approach we use for DECLARE CURSOR won't work because the statement
+ * being explained isn't necessarily a SELECT, and in particular might rewrite
+ * to multiple parsetrees.)
+ */
+static Query *
+transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
+{
+ Query *result;
+
+ if (pstate->p_variableparams)
+ {
+ List *extras_before = NIL,
+ *extras_after = NIL;
+
+ /* Since parse analysis scribbles on its input, copy the tree first! */
+ (void) transformStmt(pstate, copyObject(stmt->query),
+ &extras_before, &extras_after);
+ }
+
+ /* Now return the untransformed command as a utility Query */
+ result = makeNode(Query);
+ result->commandType = CMD_UTILITY;
+ result->utilityStmt = (Node *) stmt;
+
+ return result;
+}
+
+
/* exported so planner can check again after rewriting, query pullup, etc */
void
CheckSelectLocking(Query *qry)