diff --git a/doc/src/sgml/ref/delete.sgml b/doc/src/sgml/ref/delete.sgml
index 5d2ad3f..7acb862 100644
--- a/doc/src/sgml/ref/delete.sgml
+++ b/doc/src/sgml/ref/delete.sgml
@@ -25,6 +25,8 @@ PostgreSQL documentation
 DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table_name</replaceable> [ * ] [ [ AS ] <replaceable class="parameter">alias</replaceable> ]
     [ USING <replaceable class="PARAMETER">using_list</replaceable> ]
     [ WHERE <replaceable class="PARAMETER">condition</replaceable> | WHERE CURRENT OF <replaceable class="PARAMETER">cursor_name</replaceable> ]
+    [ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [ NULLS { FIRST | LAST } ] [, ...] ]
+    [ LIMIT { <replaceable class="parameter">count</replaceable> | ALL } ]
     [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
 </synopsis>
  </refsynopsisdiv>
@@ -56,6 +58,24 @@ DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table_name</replaceable> [ *
   </para>
 
   <para>
+   If the <literal>ORDER BY</> clause is present, the rows will be
+   processed in the specified order.  In its absence, rows are
+   processed in whichever order the system finds fastest to
+   produce.
+  </para>
+
+  <para>
+   If the <literal>LIMIT</> (or <literal>FETCH FIRST</>) clause
+   is present, processing will stop after the system has attempted
+   to delete the specified amount of rows.  In particular, if a row
+   was concurrently changed not to match the given <literal>WHERE</>
+   clause, it will count towards the <literal>LIMIT</> despite it
+   not being actually deleted.  Unlike in <literal>SELECT</>, the
+   <literal>OFFSET</literal> clause is not available in
+   <literal>DELETE</>.
+  </para>
+
+  <para>
    The optional <literal>RETURNING</> clause causes <command>DELETE</>
    to compute and return value(s) based on each row actually deleted.
    Any expression using the table's columns, and/or columns of other
diff --git a/doc/src/sgml/ref/update.sgml b/doc/src/sgml/ref/update.sgml
index 90b9208..ea75e55 100644
--- a/doc/src/sgml/ref/update.sgml
+++ b/doc/src/sgml/ref/update.sgml
@@ -27,6 +27,8 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table_name</replaceable> [ * ] [
           ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) = ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) } [, ...]
     [ FROM <replaceable class="PARAMETER">from_list</replaceable> ]
     [ WHERE <replaceable class="PARAMETER">condition</replaceable> | WHERE CURRENT OF <replaceable class="PARAMETER">cursor_name</replaceable> ]
+    [ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [ NULLS { FIRST | LAST } ] [, ...] ]
+    [ LIMIT { <replaceable class="parameter">count</replaceable> | ALL } ]
     [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
 </synopsis>
  </refsynopsisdiv>
@@ -50,6 +52,24 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table_name</replaceable> [ * ] [
   </para>
 
   <para>
+   If the <literal>ORDER BY</> clause is present, the rows will be
+   processed in the specified order.  In its absence, rows are
+   processed in whichever order the system finds fastest to
+   produce.
+  </para>
+
+  <para>
+   If the <literal>LIMIT</> (or <literal>FETCH FIRST</>) clause
+   is present, processing will stop after the system has attempted
+   to update the specified amount of rows.  In particular, if a row
+   was concurrently changed not to match the given <literal>WHERE</>
+   clause, it will count towards the <literal>LIMIT</> despite it
+   not being actually updated.  Unlike in <literal>SELECT</>, the
+   <literal>OFFSET</literal> clause is not available in
+   <literal>UPDATE</>.
+  </para>
+
+  <para>
    The optional <literal>RETURNING</> clause causes <command>UPDATE</>
    to compute and return value(s) based on each row actually updated.
    Any expression using the table's columns, and/or columns of other
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c89d808..cf9e66d 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2516,6 +2516,8 @@ _copyDeleteStmt(const DeleteStmt *from)
 	COPY_NODE_FIELD(relation);
 	COPY_NODE_FIELD(usingClause);
 	COPY_NODE_FIELD(whereClause);
+	COPY_NODE_FIELD(sortClause);
+	COPY_NODE_FIELD(limitClause);
 	COPY_NODE_FIELD(returningList);
 	COPY_NODE_FIELD(withClause);
 
@@ -2531,6 +2533,8 @@ _copyUpdateStmt(const UpdateStmt *from)
 	COPY_NODE_FIELD(targetList);
 	COPY_NODE_FIELD(whereClause);
 	COPY_NODE_FIELD(fromClause);
+	COPY_NODE_FIELD(sortClause);
+	COPY_NODE_FIELD(limitClause);
 	COPY_NODE_FIELD(returningList);
 	COPY_NODE_FIELD(withClause);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 9793cf5..aa7f203 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -894,6 +894,8 @@ _equalDeleteStmt(const DeleteStmt *a, const DeleteStmt *b)
 	COMPARE_NODE_FIELD(relation);
 	COMPARE_NODE_FIELD(usingClause);
 	COMPARE_NODE_FIELD(whereClause);
+	COMPARE_NODE_FIELD(sortClause);
+	COMPARE_NODE_FIELD(limitClause);
 	COMPARE_NODE_FIELD(returningList);
 	COMPARE_NODE_FIELD(withClause);
 
@@ -907,6 +909,8 @@ _equalUpdateStmt(const UpdateStmt *a, const UpdateStmt *b)
 	COMPARE_NODE_FIELD(targetList);
 	COMPARE_NODE_FIELD(whereClause);
 	COMPARE_NODE_FIELD(fromClause);
+	COMPARE_NODE_FIELD(sortClause);
+	COMPARE_NODE_FIELD(limitClause);
 	COMPARE_NODE_FIELD(returningList);
 	COMPARE_NODE_FIELD(withClause);
 
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 123f2a6..4f2a918 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -2970,6 +2970,10 @@ raw_expression_tree_walker(Node *node,
 					return true;
 				if (walker(stmt->whereClause, context))
 					return true;
+				if (walker(stmt->sortClause, context))
+					return true;
+				if (walker(stmt->limitClause, context))
+					return true;
 				if (walker(stmt->returningList, context))
 					return true;
 				if (walker(stmt->withClause, context))
@@ -2988,6 +2992,10 @@ raw_expression_tree_walker(Node *node,
 					return true;
 				if (walker(stmt->fromClause, context))
 					return true;
+				if (walker(stmt->sortClause, context))
+					return true;
+				if (walker(stmt->limitClause, context))
+					return true;
 				if (walker(stmt->returningList, context))
 					return true;
 				if (walker(stmt->withClause, context))
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 7225bb6..b402892 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -386,6 +386,21 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	qual = transformWhereClause(pstate, stmt->whereClause,
 								EXPR_KIND_WHERE, "WHERE");
 
+	/*
+	 * Transform the sort clause.  It might need to add items to our target
+	 * list, but that's fine.
+	 */
+	qry->targetList = NIL;
+	qry->sortClause = transformSortClause(pstate,
+										  stmt->sortClause,
+										  &qry->targetList,
+										  EXPR_KIND_ORDER_BY,
+										  true,
+										  false);
+
+	qry->limitCount = transformLimitClause(pstate, stmt->limitClause,
+										   EXPR_KIND_LIMIT, "LIMIT");
+
 	qry->returningList = transformReturningList(pstate, stmt->returningList);
 
 	/* done building the range table and jointree */
@@ -1941,8 +1956,27 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 	nsitem->p_lateral_only = false;
 	nsitem->p_lateral_ok = true;
 
-	qry->targetList = transformTargetList(pstate, stmt->targetList,
-										  EXPR_KIND_UPDATE_SOURCE);
+	/*
+	 * Transform the sort clause and the target list.  As stmt->targetList
+	 * contains the SET expressions and not actual columns of the underlying
+	 * table, we have to transform the ORDER BY first, potentially adding
+	 * resjunk columns to the target list.  Afterwards the actual SET
+	 * expressions from stmt->targetList are added to the end of the target
+	 * list.
+	 */
+	qry->targetList = NIL;
+	qry->sortClause = transformSortClause(pstate,
+										  stmt->sortClause,
+										  &qry->targetList,
+										  EXPR_KIND_ORDER_BY,
+										  true,
+										  false);
+	qry->targetList = list_concat(qry->targetList,
+								  transformTargetList(pstate,
+									  				  stmt->targetList,
+													  EXPR_KIND_UPDATE_SOURCE));
+	qry->limitCount = transformLimitClause(pstate, stmt->limitClause,
+										   EXPR_KIND_LIMIT, "LIMIT");
 
 	qual = transformWhereClause(pstate, stmt->whereClause,
 								EXPR_KIND_WHERE, "WHERE");
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 81169a4..7338d61 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -388,7 +388,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <ival>	opt_column event cursor_options opt_hold opt_set_data
 %type <objtype>	reindex_type drop_type comment_type security_label_type
 
-%type <node>	fetch_args limit_clause select_limit_value
+%type <node>	fetch_args limit_clause opt_limit_clause select_limit_value
 				offset_clause select_offset_value
 				select_offset_value2 opt_select_fetch_first_value
 %type <ival>	row_or_rows first_or_next
@@ -9121,13 +9121,15 @@ returning_clause:
  *****************************************************************************/
 
 DeleteStmt: opt_with_clause DELETE_P FROM relation_expr_opt_alias
-			using_clause where_or_current_clause returning_clause
+			using_clause where_or_current_clause opt_sort_clause opt_limit_clause returning_clause
 				{
 					DeleteStmt *n = makeNode(DeleteStmt);
 					n->relation = $4;
 					n->usingClause = $5;
 					n->whereClause = $6;
-					n->returningList = $7;
+					n->sortClause = $7;
+					n->limitClause = $8;
+					n->returningList = $9;
 					n->withClause = $1;
 					$$ = (Node *)n;
 				}
@@ -9187,6 +9189,8 @@ UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias
 			SET set_clause_list
 			from_clause
 			where_or_current_clause
+			opt_sort_clause
+			opt_limit_clause
 			returning_clause
 				{
 					UpdateStmt *n = makeNode(UpdateStmt);
@@ -9194,7 +9198,9 @@ UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias
 					n->targetList = $5;
 					n->fromClause = $6;
 					n->whereClause = $7;
-					n->returningList = $8;
+					n->sortClause = $8;
+					n->limitClause = $9;
+					n->returningList = $10;
 					n->withClause = $1;
 					$$ = (Node *)n;
 				}
@@ -9700,6 +9706,11 @@ limit_clause:
 				{ $$ = $3; }
 		;
 
+opt_limit_clause:
+			limit_clause		{ $$ = $1; }
+			| /* EMPTY */		{ $$ = NULL; }
+		;
+
 offset_clause:
 			OFFSET select_offset_value
 				{ $$ = $2; }
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b5011af..ecf634d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1043,6 +1043,8 @@ typedef struct DeleteStmt
 	RangeVar   *relation;		/* relation to delete from */
 	List	   *usingClause;	/* optional using clause for more tables */
 	Node	   *whereClause;	/* qualifications */
+	List	   *sortClause;		/* ORDER BY */
+	Node	   *limitClause;	/* LIMIT */
 	List	   *returningList;	/* list of expressions to return */
 	WithClause *withClause;		/* WITH clause */
 } DeleteStmt;
@@ -1058,6 +1060,8 @@ typedef struct UpdateStmt
 	List	   *targetList;		/* the target list (of ResTarget) */
 	Node	   *whereClause;	/* qualifications */
 	List	   *fromClause;		/* optional from clause for more tables */
+	List	   *sortClause;		/* ORDER BY */
+	Node	   *limitClause;	/* LIMIT */
 	List	   *returningList;	/* list of expressions to return */
 	WithClause *withClause;		/* WITH clause */
 } UpdateStmt;
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index 71b856f..2f88190 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -40,6 +40,65 @@ SELECT * FROM update_test;
  10 | 20 | 
 (2 rows)
 
+-- test ORDER BY
+EXPLAIN (COSTS OFF) UPDATE update_test SET b = 10 ORDER BY b;
+             QUERY PLAN              
+-------------------------------------
+ Update on update_test
+   ->  Sort
+         Sort Key: b
+         ->  Seq Scan on update_test
+(4 rows)
+
+EXPLAIN (COSTS OFF) UPDATE update_test t SET b = 10 ORDER BY t.b;
+              QUERY PLAN               
+---------------------------------------
+ Update on update_test t
+   ->  Sort
+         Sort Key: b
+         ->  Seq Scan on update_test t
+(4 rows)
+
+EXPLAIN (COSTS OFF) UPDATE update_test SET b = 10 ORDER BY columndoesnotexist;
+ERROR:  column "columndoesnotexist" does not exist
+LINE 1: ...COSTS OFF) UPDATE update_test SET b = 10 ORDER BY columndoes...
+                                                             ^
+EXPLAIN (COSTS OFF) UPDATE update_test t SET b = 10 ORDER BY t.b + 0;
+              QUERY PLAN               
+---------------------------------------
+ Update on update_test t
+   ->  Sort
+         Sort Key: ((b + 0))
+         ->  Seq Scan on update_test t
+(4 rows)
+
+EXPLAIN (COSTS OFF) UPDATE update_test t SET b = 20 WHERE a = 10 ORDER BY t.b + 0;
+              QUERY PLAN               
+---------------------------------------
+ Update on update_test t
+   ->  Sort
+         Sort Key: ((b + 0))
+         ->  Seq Scan on update_test t
+               Filter: (a = 10)
+(5 rows)
+
+EXPLAIN (COSTS OFF) UPDATE update_test t1 SET b = 20 FROM update_test t2 WHERE t1.a = t2.a ORDER BY t2.b;
+                     QUERY PLAN                     
+----------------------------------------------------
+ Update on update_test t1
+   ->  Sort
+         Sort Key: t2.b
+         ->  Merge Join
+               Merge Cond: (t1.a = t2.a)
+               ->  Sort
+                     Sort Key: t1.a
+                     ->  Seq Scan on update_test t1
+               ->  Sort
+                     Sort Key: t2.a
+                     ->  Seq Scan on update_test t2
+(11 rows)
+
+UPDATE update_test t SET b = 20 WHERE a = 10 ORDER BY t.b + 0;
 --
 -- Test VALUES in FROM
 --
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index a8a028f..0c91f8f 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -27,6 +27,32 @@ UPDATE update_test t SET b = t.b + 10 WHERE t.a = 10;
 SELECT * FROM update_test;
 
 --
+-- test ORDER BY
+--
+EXPLAIN (COSTS OFF) UPDATE update_test SET b = 10 ORDER BY b;
+
+EXPLAIN (COSTS OFF) UPDATE update_test t SET b = 10 ORDER BY t.b;
+
+EXPLAIN (COSTS OFF) UPDATE update_test SET b = 10 ORDER BY columndoesnotexist;
+
+EXPLAIN (COSTS OFF) UPDATE update_test t SET b = 10 ORDER BY t.b + 0;
+
+EXPLAIN (COSTS OFF) UPDATE update_test t SET b = 20 WHERE a = 10 ORDER BY t.b + 0;
+
+EXPLAIN (COSTS OFF) UPDATE update_test t1 SET b = 20 FROM update_test t2 WHERE t1.a = t2.a ORDER BY t2.b;
+
+UPDATE update_test t SET b = 20 WHERE a = 10 ORDER BY t.b + 0;
+
+--
+-- test LIMIT
+--
+EXPLAIN (COSTS OFF) UPDATE update_test SET b = 10 LIMIT 1;
+
+EXPLAIN (COSTS OFF) UPDATE update_test SET b = 10 ORDER BY b LIMIT 1;
+
+UPDATE update_test SET b = 0 LIMIT 0 RETURNING *;
+
+--
 -- Test VALUES in FROM
 --
 
