diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 104 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 48 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 18 | ||||
-rw-r--r-- | src/backend/parser/parse_type.c | 4 |
4 files changed, 107 insertions, 67 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 3cb64dc8249..ee6bfe6ae92 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.320 2005/04/14 20:03:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.321 2005/04/28 21:47:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -134,7 +134,7 @@ static void transformFKConstraints(ParseState *pstate, bool isAddConstraint); static void applyColumnNames(List *dst, List *src); static List *getSetColTypes(ParseState *pstate, Node *node); -static void transformForUpdate(Query *qry, List *forUpdate); +static void transformLocking(Query *qry, List *lockedRels, bool forUpdate); static void transformConstraintAttrs(List *constraintList); static void transformColumnType(ParseState *pstate, ColumnDef *column); static void release_pstate_resources(ParseState *pstate); @@ -1810,8 +1810,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) qry->commandType = CMD_SELECT; - /* make FOR UPDATE clause available to addRangeTableEntry */ - pstate->p_forUpdate = stmt->forUpdate; + /* make FOR UPDATE/FOR SHARE list available to addRangeTableEntry */ + pstate->p_lockedRels = stmt->lockedRels; /* process the FROM clause */ transformFromClause(pstate, stmt->fromClause); @@ -1870,8 +1870,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); - if (stmt->forUpdate != NIL) - transformForUpdate(qry, stmt->forUpdate); + if (stmt->lockedRels != NIL) + transformLocking(qry, stmt->lockedRels, stmt->forUpdate); return qry; } @@ -1899,7 +1899,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) List *sortClause; Node *limitOffset; Node *limitCount; - List *forUpdate; + List *lockedRels; + bool forUpdate; Node *node; ListCell *left_tlist, *dtlist; @@ -1937,18 +1938,19 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) sortClause = stmt->sortClause; limitOffset = stmt->limitOffset; limitCount = stmt->limitCount; + lockedRels = stmt->lockedRels; forUpdate = stmt->forUpdate; stmt->sortClause = NIL; stmt->limitOffset = NULL; stmt->limitCount = NULL; - stmt->forUpdate = NIL; + stmt->lockedRels = NIL; - /* We don't support forUpdate with set ops at the moment. */ - if (forUpdate) + /* We don't support FOR UPDATE/SHARE with set ops at the moment. */ + if (lockedRels) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); + errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); /* * Recursively transform the components of the tree. @@ -2083,8 +2085,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); - if (forUpdate != NIL) - transformForUpdate(qry, forUpdate); + if (lockedRels != NIL) + transformLocking(qry, lockedRels, forUpdate); return qry; } @@ -2107,11 +2109,11 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"))); - /* We don't support forUpdate with set ops at the moment. */ - if (stmt->forUpdate) + /* We don't support FOR UPDATE/SHARE with set ops at the moment. */ + if (stmt->lockedRels) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); + errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); /* * If an internal node of a set-op tree has ORDER BY, UPDATE, or LIMIT @@ -2128,7 +2130,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt) { Assert(stmt->larg != NULL && stmt->rarg != NULL); if (stmt->sortClause || stmt->limitOffset || stmt->limitCount || - stmt->forUpdate) + stmt->lockedRels) isLeaf = true; else isLeaf = false; @@ -2711,47 +2713,67 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt) /* exported so planner can check again after rewriting, query pullup, etc */ void -CheckSelectForUpdate(Query *qry) +CheckSelectLocking(Query *qry, bool forUpdate) { + const char *operation; + + if (forUpdate) + operation = "SELECT FOR UPDATE"; + else + operation = "SELECT FOR SHARE"; + if (qry->setOperations) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); + /* translator: %s is a SQL command, like SELECT FOR UPDATE */ + errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", operation))); if (qry->distinctClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE is not allowed with DISTINCT clause"))); + /* translator: %s is a SQL command, like SELECT FOR UPDATE */ + errmsg("%s is not allowed with DISTINCT clause", operation))); if (qry->groupClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE is not allowed with GROUP BY clause"))); + /* translator: %s is a SQL command, like SELECT FOR UPDATE */ + errmsg("%s is not allowed with GROUP BY clause", operation))); if (qry->havingQual != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE is not allowed with HAVING clause"))); + /* translator: %s is a SQL command, like SELECT FOR UPDATE */ + errmsg("%s is not allowed with HAVING clause", operation))); if (qry->hasAggs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE is not allowed with aggregate functions"))); + /* translator: %s is a SQL command, like SELECT FOR UPDATE */ + errmsg("%s is not allowed with aggregate functions", operation))); } /* - * Convert FOR UPDATE name list into rowMarks list of integer relids + * Convert FOR UPDATE/SHARE name list into rowMarks list of integer relids * - * NB: if you need to change this, see also markQueryForUpdate() + * NB: if you need to change this, see also markQueryForLocking() * in rewriteHandler.c. */ static void -transformForUpdate(Query *qry, List *forUpdate) +transformLocking(Query *qry, List *lockedRels, bool forUpdate) { - List *rowMarks = qry->rowMarks; + List *rowMarks; ListCell *l; ListCell *rt; Index i; - CheckSelectForUpdate(qry); + if (qry->rowMarks && forUpdate != qry->forUpdate) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot use both FOR UPDATE and FOR SHARE in one query"))); + qry->forUpdate = forUpdate; + + CheckSelectLocking(qry, forUpdate); + + rowMarks = qry->rowMarks; - if (linitial(forUpdate) == NULL) + if (linitial(lockedRels) == NULL) { /* all regular tables used in query */ i = 0; @@ -2770,10 +2792,11 @@ transformForUpdate(Query *qry, List *forUpdate) case RTE_SUBQUERY: /* - * FOR UPDATE of subquery is propagated to subquery's - * rels + * FOR UPDATE/SHARE of subquery is propagated to all + * of subquery's rels */ - transformForUpdate(rte->subquery, list_make1(NULL)); + transformLocking(rte->subquery, list_make1(NULL), + forUpdate); break; default: /* ignore JOIN, SPECIAL, FUNCTION RTEs */ @@ -2784,7 +2807,7 @@ transformForUpdate(Query *qry, List *forUpdate) else { /* just the named tables */ - foreach(l, forUpdate) + foreach(l, lockedRels) { char *relname = strVal(lfirst(l)); @@ -2806,25 +2829,26 @@ transformForUpdate(Query *qry, List *forUpdate) case RTE_SUBQUERY: /* - * FOR UPDATE of subquery is propagated to - * subquery's rels + * FOR UPDATE/SHARE of subquery is propagated to + * all of subquery's rels */ - transformForUpdate(rte->subquery, list_make1(NULL)); + transformLocking(rte->subquery, list_make1(NULL), + forUpdate); break; case RTE_JOIN: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE cannot be applied to a join"))); + errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a join"))); break; case RTE_SPECIAL: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE cannot be applied to NEW or OLD"))); + errmsg("SELECT FOR UPDATE/SHARE cannot be applied to NEW or OLD"))); break; case RTE_FUNCTION: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE cannot be applied to a function"))); + errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function"))); break; default: elog(ERROR, "unrecognized RTE type: %d", @@ -2837,7 +2861,7 @@ transformForUpdate(Query *qry, List *forUpdate) if (rt == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" in FOR UPDATE clause not found in FROM clause", + errmsg("relation \"%s\" in FOR UPDATE/SHARE clause not found in FROM clause", relname))); } } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 260a2e1b455..c51c10c7a89 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.488 2005/04/23 17:22:16 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.489 2005/04/28 21:47:14 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -87,7 +87,7 @@ static List *check_func_name(List *names); static List *extractArgTypes(List *parameters); static SelectStmt *findLeftmostSelect(SelectStmt *node); static void insertSelectOptions(SelectStmt *stmt, - List *sortClause, List *forUpdate, + List *sortClause, List *lockingClause, Node *limitOffset, Node *limitCount); static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg); static Node *doNegate(Node *n); @@ -242,7 +242,8 @@ static void doNegateFloat(Value *v); %type <oncommit> OnCommitOption %type <withoids> OptWithOids WithOidsAs -%type <list> for_update_clause opt_for_update_clause update_list +%type <list> for_locking_clause opt_for_locking_clause + update_list %type <boolean> opt_all %type <node> join_outer join_qual @@ -4886,9 +4887,9 @@ select_with_parens: ; /* - * FOR UPDATE may be before or after LIMIT/OFFSET. + * FOR UPDATE/SHARE may be before or after LIMIT/OFFSET. * In <=7.2.X, LIMIT/OFFSET had to be after FOR UPDATE - * We now support both orderings, but prefer LIMIT/OFFSET before FOR UPDATE + * We now support both orderings, but prefer LIMIT/OFFSET before FOR UPDATE/SHARE * 2002-08-28 bjm */ select_no_parens: @@ -4899,13 +4900,13 @@ select_no_parens: NULL, NULL); $$ = $1; } - | select_clause opt_sort_clause for_update_clause opt_select_limit + | select_clause opt_sort_clause for_locking_clause opt_select_limit { insertSelectOptions((SelectStmt *) $1, $2, $3, list_nth($4, 0), list_nth($4, 1)); $$ = $1; } - | select_clause opt_sort_clause select_limit opt_for_update_clause + | select_clause opt_sort_clause select_limit opt_for_locking_clause { insertSelectOptions((SelectStmt *) $1, $2, $4, list_nth($3, 0), list_nth($3, 1)); @@ -5146,13 +5147,14 @@ having_clause: | /*EMPTY*/ { $$ = NULL; } ; -for_update_clause: - FOR UPDATE update_list { $$ = $3; } +for_locking_clause: + FOR UPDATE update_list { $$ = lcons(makeString("for_update"), $3); } + | FOR SHARE update_list { $$ = lcons(makeString("for_share"), $3); } | FOR READ ONLY { $$ = NULL; } ; -opt_for_update_clause: - for_update_clause { $$ = $1; } +opt_for_locking_clause: + for_locking_clause { $$ = $1; } | /* EMPTY */ { $$ = NULL; } ; @@ -8379,7 +8381,7 @@ findLeftmostSelect(SelectStmt *node) */ static void insertSelectOptions(SelectStmt *stmt, - List *sortClause, List *forUpdate, + List *sortClause, List *lockingClause, Node *limitOffset, Node *limitCount) { /* @@ -8394,13 +8396,27 @@ insertSelectOptions(SelectStmt *stmt, errmsg("multiple ORDER BY clauses not allowed"))); stmt->sortClause = sortClause; } - if (forUpdate) + if (lockingClause) { - if (stmt->forUpdate) + Value *type; + + if (stmt->lockedRels) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("multiple FOR UPDATE clauses not allowed"))); - stmt->forUpdate = forUpdate; + errmsg("multiple FOR UPDATE/FOR SHARE clauses not allowed"))); + + Assert(list_length(lockingClause) > 1); + /* 1st is Value node containing "for_update" or "for_share" */ + type = (Value *) linitial(lockingClause); + Assert(IsA(type, String)); + if (strcmp(strVal(type), "for_update") == 0) + stmt->forUpdate = true; + else if (strcmp(strVal(type), "for_share") == 0) + stmt->forUpdate = false; + else + elog(ERROR, "invalid first node in locking clause"); + + stmt->lockedRels = list_delete_first(lockingClause); } if (limitOffset) { diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index b68ae005150..d1e5fca2aae 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.106 2005/04/13 16:50:55 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.107 2005/04/28 21:47:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,7 +40,7 @@ static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, Oid relid); static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, RangeTblEntry *rte1, const char *aliasname1); -static bool isForUpdate(ParseState *pstate, char *refname); +static bool isLockedRel(ParseState *pstate, char *refname); static void expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, bool include_dropped, @@ -759,9 +759,9 @@ addRangeTableEntry(ParseState *pstate, * Get the rel's OID. This access also ensures that we have an * up-to-date relcache entry for the rel. Since this is typically the * first access to a rel in a statement, be careful to get the right - * access level depending on whether we're doing SELECT FOR UPDATE. + * access level depending on whether we're doing SELECT FOR UPDATE/SHARE. */ - lockmode = isForUpdate(pstate, refname) ? RowShareLock : AccessShareLock; + lockmode = isLockedRel(pstate, refname) ? RowShareLock : AccessShareLock; rel = heap_openrv(relation, lockmode); rte->relid = RelationGetRelid(rel); @@ -1121,17 +1121,17 @@ addRangeTableEntryForJoin(ParseState *pstate, } /* - * Has the specified refname been selected FOR UPDATE? + * Has the specified refname been selected FOR UPDATE/FOR SHARE? */ static bool -isForUpdate(ParseState *pstate, char *refname) +isLockedRel(ParseState *pstate, char *refname) { /* Outer loop to check parent query levels as well as this one */ while (pstate != NULL) { - if (pstate->p_forUpdate != NIL) + if (pstate->p_lockedRels != NIL) { - if (linitial(pstate->p_forUpdate) == NULL) + if (linitial(pstate->p_lockedRels) == NULL) { /* all tables used in query */ return true; @@ -1141,7 +1141,7 @@ isForUpdate(ParseState *pstate, char *refname) /* just the named tables */ ListCell *l; - foreach(l, pstate->p_forUpdate) + foreach(l, pstate->p_lockedRels) { char *rname = strVal(lfirst(l)); diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index aae7908f507..21ce20c5ecf 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.73 2004/12/31 22:00:27 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.74 2005/04/28 21:47:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -432,7 +432,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod) stmt->sortClause != NIL || stmt->limitOffset != NULL || stmt->limitCount != NULL || - stmt->forUpdate != NIL || + stmt->lockedRels != NIL || stmt->op != SETOP_NONE) goto fail; if (list_length(stmt->targetList) != 1) |