Disallow FOR UPDATE/SHARE for queries using SQL JOIN syntax.
authorPavan Deolasee <[email protected]>
Fri, 12 Aug 2016 09:32:29 +0000 (15:02 +0530)
committerPavan Deolasee <[email protected]>
Fri, 12 Aug 2016 09:32:29 +0000 (15:02 +0530)
Shaun Thomas had reported the original and our last attempt to block FOR
UPDATE/SHARE when invoked on more than one table wasn't enough. Hopefully
this takes care of all such occurances of the query. But we now allow FOR
UPDATE/SHARE if all tables involved in the query are replicated tables

src/backend/optimizer/plan/planner.c
src/backend/parser/analyze.c
src/test/regress/expected/xc_for_update.out
src/test/regress/sql/xc_for_update.sql

index 2f377159eb9178c7d3807c884abaceee4a035044..2b4e17c3adbc966029398ed712bf41e0154af2bc 100644 (file)
@@ -3094,6 +3094,32 @@ preprocess_rowmarks(PlannerInfo *root)
                 */
                CheckSelectLocking(parse, ((RowMarkClause *)
                                                                   linitial(parse->rowMarks))->strength);
+#ifdef XCP
+               if (parse->jointree)
+               {
+                       Bitmapset  *baserels = get_base_rel_indexes((Node *) parse->jointree);;
+                       int x, num_rels = 0;
+                       bool dist_found = false;
+
+                       while ((x = bms_first_member(baserels)) >= 0)
+                       {
+                               RangeTblEntry *rte = rt_fetch(x, parse->rtable);
+                               RelationLocInfo *locinfo = NULL;
+                               if (OidIsValid(rte->relid))
+                                       locinfo = GetRelationLocInfo(rte->relid);
+                               if (locinfo && !IsRelationReplicated(locinfo))
+                                       dist_found = true;
+                               num_rels++;
+                       }
+
+                       if (dist_found && num_rels > 1)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("%s is not allowed with joins",
+                                                        LCS_asString(((RowMarkClause *)
+                                                                        linitial(parse->rowMarks))->strength))));
+               }
+#endif
        }
        else
        {
@@ -3155,6 +3181,7 @@ preprocess_rowmarks(PlannerInfo *root)
                prowmarks = lappend(prowmarks, newrc);
        }
 
+
        /*
         * Now, add rowmarks for any non-target, non-locked base relations.
         */
index 6d5392b4074afb9c1663be58121edee5c1dc1f21..847e5a363439bef45b18c3ad6de40642024a7bca 100644 (file)
@@ -2713,15 +2713,6 @@ CheckSelectLocking(Query *qry, LockClauseStrength strength)
                  translator: %s is a SQL row locking clause such as FOR UPDATE */
                                 errmsg("%s is not allowed with window functions",
                                                LCS_asString(strength))));
-#ifdef XCP
-       if (list_length(qry->jointree->fromlist) > 1)
-               ereport(ERROR,
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-               /*------
-                 translator: %s is a SQL row locking clause such as FOR UPDATE */
-                                errmsg("%s is not allowed with joins",
-                                               LCS_asString(strength))));
-#endif
 
        if (expression_returns_set((Node *) qry->targetList))
                ereport(ERROR,
index 2dd78ad5ce806cdb66dd0f63aa5396c8628fe811..5d4476bd2ff6fd86c323cefda39310e346111cde 100644 (file)
@@ -159,6 +159,18 @@ explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2
 ERROR:  FOR SHARE is not allowed with joins
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2 for share of t2;
 ERROR:  FOR SHARE is not allowed with joins
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 join t2 on (t1.val = t2.val) for share;
+ERROR:  FOR SHARE is not allowed with joins
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 left join t2 on (t1.val = t2.val) for share;
+ERROR:  FOR SHARE is not allowed with joins
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 right join t2 on (t1.val = t2.val) for share;
+ERROR:  FOR SHARE is not allowed with joins
+select * from t1 join t2 on (t1.val = t2.val) for share;
+ERROR:  FOR SHARE is not allowed with joins
+select * from t1 left join t2 on (t1.val = t2.val) for share;
+ERROR:  FOR SHARE is not allowed with joins
+select * from t1 right join t2 on (t1.val = t2.val) for share;
+ERROR:  FOR SHARE is not allowed with joins
 -- three table case
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2, t3;
                          QUERY PLAN                          
@@ -195,6 +207,72 @@ explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2,
 ERROR:  FOR UPDATE is not allowed with joins
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2, t3 for share of t1,t2 nowait;
 ERROR:  FOR SHARE is not allowed with joins
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2);
+                             QUERY PLAN                              
+---------------------------------------------------------------------
+ Remote Subquery Scan on all
+   Output: t1.val, t1.val2, t2.val, t2.val2, t3.val, t3.val2
+   ->  Merge Join
+         Output: t1.val, t1.val2, t2.val, t2.val2, t3.val, t3.val2
+         Merge Cond: (t3.val2 = t1.val2)
+         ->  Remote Subquery Scan on all
+               Output: t3.val, t3.val2
+               Distribute results by H: val2
+               ->  Sort
+                     Output: t3.val, t3.val2
+                     Sort Key: t3.val2
+                     ->  Seq Scan on public.t3
+                           Output: t3.val, t3.val2
+         ->  Materialize
+               Output: t1.val, t1.val2, t2.val, t2.val2
+               ->  Merge Join
+                     Output: t1.val, t1.val2, t2.val, t2.val2
+                     Merge Cond: (t1.val2 = t2.val2)
+                     ->  Remote Subquery Scan on all
+                           Output: t1.val, t1.val2
+                           Distribute results by H: val2
+                           ->  Sort
+                                 Output: t1.val, t1.val2
+                                 Sort Key: t1.val2
+                                 ->  Seq Scan on public.t1
+                                       Output: t1.val, t1.val2
+                     ->  Materialize
+                           Output: t2.val, t2.val2
+                           ->  Remote Subquery Scan on all
+                                 Output: t2.val, t2.val2
+                                 Distribute results by H: val2
+                                 ->  Sort
+                                       Output: t2.val, t2.val2
+                                       Sort Key: t2.val2
+                                       ->  Seq Scan on public.t2
+                                             Output: t2.val, t2.val2
+(36 rows)
+
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update;
+ERROR:  FOR UPDATE is not allowed with joins
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update of t1;
+ERROR:  FOR UPDATE is not allowed with joins
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update of t1,t3;
+ERROR:  FOR UPDATE is not allowed with joins
+select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2);
+ val | val2 | val | val2 | val | val2 
+-----+------+-----+------+-----+------
+   1 |   11 |   3 |   11 |   5 |   11
+   1 |   11 |   4 |   11 |   5 |   11
+   2 |   11 |   3 |   11 |   5 |   11
+   2 |   11 |   4 |   11 |   5 |   11
+   1 |   11 |   3 |   11 |   6 |   11
+   1 |   11 |   4 |   11 |   6 |   11
+   2 |   11 |   3 |   11 |   6 |   11
+   2 |   11 |   4 |   11 |   6 |   11
+(8 rows)
+
+select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update;
+ERROR:  FOR UPDATE is not allowed with joins
+select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update of t1;
+ERROR:  FOR UPDATE is not allowed with joins
+select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update of t1,t3;
+ERROR:  FOR UPDATE is not allowed with joins
 -- check a few subquery cases
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from (select * from t1 for update of t1 nowait) as foo;
                       QUERY PLAN                      
@@ -210,30 +288,7 @@ explain (costs off, num_nodes off, nodes off, verbose on)  select * from (select
 (8 rows)
 
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 where val in (select val from t2 for update of t2 nowait) for update;
-                                  QUERY PLAN                                  
-------------------------------------------------------------------------------
- Remote Subquery Scan on all
-   Output: t1.val, t1.val2, t1.ctid, "ANY_subquery".*
-   ->  LockRows
-         Output: t1.val, t1.val2, t1.ctid, "ANY_subquery".*
-         ->  Hash Join
-               Output: t1.val, t1.val2, t1.ctid, "ANY_subquery".*
-               Hash Cond: (t1.val = "ANY_subquery".val)
-               ->  Seq Scan on public.t1
-                     Output: t1.val, t1.val2, t1.ctid
-               ->  Hash
-                     Output: "ANY_subquery".*, "ANY_subquery".val
-                     ->  HashAggregate
-                           Output: "ANY_subquery".*, "ANY_subquery".val
-                           Group Key: "ANY_subquery".val
-                           ->  Subquery Scan on "ANY_subquery"
-                                 Output: "ANY_subquery".*, "ANY_subquery".val
-                                 ->  LockRows
-                                       Output: t2.val, t2.ctid
-                                       ->  Seq Scan on public.t2
-                                             Output: t2.val, t2.ctid
-(20 rows)
-
+ERROR:  FOR UPDATE is not allowed with joins
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 where val in (select val from t2 for update of t2 nowait);
                           QUERY PLAN                           
 ---------------------------------------------------------------
@@ -478,10 +533,7 @@ ERROR:  FOR SHARE is not allowed with joins
 (2 rows)
 
  select * from t1 where val in (select val from t2 for update of t2 nowait) for update;
- val | val2 
------+------
-(0 rows)
-
+ERROR:  FOR UPDATE is not allowed with joins
  select * from t1 where val in (select val from t2 for update of t2 nowait);
  val | val2 
 -----+------
@@ -923,12 +975,24 @@ commit prepared 'pt_1';
 -- ****  
 begin;
   select * from t1, t2, t3 order by 1 for update;
-ERROR:  FOR UPDATE is not allowed with joins
+ val | val2 | val | val2 | val | val2 
+-----+------+-----+------+-----+------
+   1 |   11 |   3 |   11 |   5 |   11
+   1 |   11 |   3 |   11 |   6 |   11
+   1 |   11 |   4 |   11 |   5 |   11
+   1 |   11 |   4 |   11 |   6 |   11
+   2 |   11 |   3 |   11 |   5 |   11
+   2 |   11 |   3 |   11 |   6 |   11
+   2 |   11 |   4 |   11 |   5 |   11
+   2 |   11 |   4 |   11 |   6 |   11
+(8 rows)
+
 prepare transaction 'pt_1';
 select gid from pg_prepared_xacts where gid = 'pt_1';
- gid 
------
-(0 rows)
+ gid  
+------
+ pt_1
+(1 row)
 
 select is_prepared_on_node('pt_1', 1); -- true
  is_prepared_on_node 
@@ -937,7 +1001,6 @@ select is_prepared_on_node('pt_1', 1); -- true
 (1 row)
 
 commit prepared 'pt_1';
-ERROR:  prepared transaction with identifier "pt_1" does not exist
 -- ****  
 begin;
   --WITH q1 AS (SELECT * from t1 order by 1 FOR UPDATE) SELECT * FROM q1,t2 order by 1 FOR UPDATE;
@@ -1004,12 +1067,16 @@ commit prepared 'pt_1';
 -- ****  
 begin;
   select * from t1, t2 where t1.val = t2.val for share;
-ERROR:  FOR SHARE is not allowed with joins
+ val | val2 | val | val2 
+-----+------+-----+------
+(0 rows)
+
 prepare transaction 'pt_1';
 select gid from pg_prepared_xacts where gid = 'pt_1';
- gid 
------
-(0 rows)
+ gid  
+------
+ pt_1
+(1 row)
 
 select is_prepared_on_node('pt_1', 1); -- true
  is_prepared_on_node 
@@ -1018,16 +1085,23 @@ select is_prepared_on_node('pt_1', 1); -- true
 (1 row)
 
 commit prepared 'pt_1';
-ERROR:  prepared transaction with identifier "pt_1" does not exist
 -- ****  
 begin;
   select * from t1, t2 for share of t2;
-ERROR:  FOR SHARE is not allowed with joins
+ val | val2 | val | val2 
+-----+------+-----+------
+   1 |   11 |   3 |   11
+   1 |   11 |   4 |   11
+   2 |   11 |   3 |   11
+   2 |   11 |   4 |   11
+(4 rows)
+
 prepare transaction 'pt_1';
 select gid from pg_prepared_xacts where gid = 'pt_1';
- gid 
------
-(0 rows)
+ gid  
+------
+ pt_1
+(1 row)
 
 select is_prepared_on_node('pt_1', 1); -- true
  is_prepared_on_node 
@@ -1036,7 +1110,6 @@ select is_prepared_on_node('pt_1', 1); -- true
 (1 row)
 
 commit prepared 'pt_1';
-ERROR:  prepared transaction with identifier "pt_1" does not exist
 -- ****  
 begin;
   select * from (select * from t1 for update of t1 nowait) as foo;
@@ -1105,12 +1178,20 @@ commit prepared 'pt_1';
 -- ****  
 begin;
   select * from t1, t2 for share of t2 for update of t1;
-ERROR:  FOR SHARE is not allowed with joins
+ val | val2 | val | val2 
+-----+------+-----+------
+   1 |   11 |   3 |   11
+   1 |   11 |   4 |   11
+   2 |   11 |   3 |   11
+   2 |   11 |   4 |   11
+(4 rows)
+
 prepare transaction 'pt_1';
 select gid from pg_prepared_xacts where gid = 'pt_1';
- gid 
------
-(0 rows)
+ gid  
+------
+ pt_1
+(1 row)
 
 select is_prepared_on_node('pt_1', 1); -- true
  is_prepared_on_node 
@@ -1119,7 +1200,6 @@ select is_prepared_on_node('pt_1', 1); -- true
 (1 row)
 
 commit prepared 'pt_1';
-ERROR:  prepared transaction with identifier "pt_1" does not exist
 -- ****  
 begin;
   select * from t1 for share of t1 for update of t1;
@@ -1238,12 +1318,20 @@ commit prepared 'pt_1';
 -- ****  
 begin;
   select * from t1 a,t1 b for share of a for update of b;
-ERROR:  FOR SHARE is not allowed with joins
+ val | val2 | val | val2 
+-----+------+-----+------
+   1 |   11 |   1 |   11
+   1 |   11 |   2 |   11
+   2 |   11 |   1 |   11
+   2 |   11 |   2 |   11
+(4 rows)
+
 prepare transaction 'pt_1';
 select gid from pg_prepared_xacts where gid = 'pt_1';
- gid 
------
-(0 rows)
+ gid  
+------
+ pt_1
+(1 row)
 
 select is_prepared_on_node('pt_1', 1); -- true
  is_prepared_on_node 
@@ -1252,7 +1340,6 @@ select is_prepared_on_node('pt_1', 1); -- true
 (1 row)
 
 commit prepared 'pt_1';
-ERROR:  prepared transaction with identifier "pt_1" does not exist
 -- ****  
 begin;
   select * from p1 order by 1 for update;
@@ -1379,12 +1466,24 @@ commit prepared 'pt_1';
 -- ****  
 begin;
   select * from t1, t2, t3 order by 1 for update;
-ERROR:  FOR UPDATE is not allowed with joins
+ val | val2 | val | val2 | val | val2 
+-----+------+-----+------+-----+------
+   1 |   11 |   3 |   11 |   5 |   11
+   1 |   11 |   3 |   11 |   6 |   11
+   1 |   11 |   4 |   11 |   5 |   11
+   1 |   11 |   4 |   11 |   6 |   11
+   2 |   11 |   3 |   11 |   5 |   11
+   2 |   11 |   3 |   11 |   6 |   11
+   2 |   11 |   4 |   11 |   5 |   11
+   2 |   11 |   4 |   11 |   6 |   11
+(8 rows)
+
 prepare transaction 'pt_1';
 select gid from pg_prepared_xacts where gid = 'pt_1';
- gid 
------
-(0 rows)
+ gid  
+------
+ pt_1
+(1 row)
 
 select is_prepared_on_node('pt_1', 1); -- true
  is_prepared_on_node 
@@ -1393,7 +1492,6 @@ select is_prepared_on_node('pt_1', 1); -- true
 (1 row)
 
 commit prepared 'pt_1';
-ERROR:  prepared transaction with identifier "pt_1" does not exist
 -- ****  
 begin;
   --WITH q1 AS (SELECT * from t1 order by 1 FOR UPDATE) SELECT * FROM q1,t2 order by 1 FOR UPDATE;
@@ -1460,12 +1558,16 @@ commit prepared 'pt_1';
 -- ****  
 begin;
   select * from t1, t2 where t1.val = t2.val for share;
-ERROR:  FOR SHARE is not allowed with joins
+ val | val2 | val | val2 
+-----+------+-----+------
+(0 rows)
+
 prepare transaction 'pt_1';
 select gid from pg_prepared_xacts where gid = 'pt_1';
- gid 
------
-(0 rows)
+ gid  
+------
+ pt_1
+(1 row)
 
 select is_prepared_on_node('pt_1', 1); -- true
  is_prepared_on_node 
@@ -1474,16 +1576,23 @@ select is_prepared_on_node('pt_1', 1); -- true
 (1 row)
 
 commit prepared 'pt_1';
-ERROR:  prepared transaction with identifier "pt_1" does not exist
 -- ****  
 begin;
   select * from t1, t2 for share of t2;
-ERROR:  FOR SHARE is not allowed with joins
+ val | val2 | val | val2 
+-----+------+-----+------
+   1 |   11 |   3 |   11
+   1 |   11 |   4 |   11
+   2 |   11 |   3 |   11
+   2 |   11 |   4 |   11
+(4 rows)
+
 prepare transaction 'pt_1';
 select gid from pg_prepared_xacts where gid = 'pt_1';
- gid 
------
-(0 rows)
+ gid  
+------
+ pt_1
+(1 row)
 
 select is_prepared_on_node('pt_1', 1); -- true
  is_prepared_on_node 
@@ -1492,7 +1601,6 @@ select is_prepared_on_node('pt_1', 1); -- true
 (1 row)
 
 commit prepared 'pt_1';
-ERROR:  prepared transaction with identifier "pt_1" does not exist
 -- ****  
 begin;
   select * from (select * from t1 for update of t1 nowait) as foo;
@@ -1561,12 +1669,20 @@ commit prepared 'pt_1';
 -- ****  
 begin;
   select * from t1, t2 for share of t2 for update of t1;
-ERROR:  FOR SHARE is not allowed with joins
+ val | val2 | val | val2 
+-----+------+-----+------
+   1 |   11 |   3 |   11
+   1 |   11 |   4 |   11
+   2 |   11 |   3 |   11
+   2 |   11 |   4 |   11
+(4 rows)
+
 prepare transaction 'pt_1';
 select gid from pg_prepared_xacts where gid = 'pt_1';
- gid 
------
-(0 rows)
+ gid  
+------
+ pt_1
+(1 row)
 
 select is_prepared_on_node('pt_1', 1); -- true
  is_prepared_on_node 
@@ -1575,7 +1691,6 @@ select is_prepared_on_node('pt_1', 1); -- true
 (1 row)
 
 commit prepared 'pt_1';
-ERROR:  prepared transaction with identifier "pt_1" does not exist
 -- ****  
 begin;
   select * from t1 for share of t1 for update of t1;
@@ -1694,12 +1809,20 @@ commit prepared 'pt_1';
 -- ****  
 begin;
   select * from t1 a,t1 b for share of a for update of b;
-ERROR:  FOR SHARE is not allowed with joins
+ val | val2 | val | val2 
+-----+------+-----+------
+   1 |   11 |   1 |   11
+   1 |   11 |   2 |   11
+   2 |   11 |   1 |   11
+   2 |   11 |   2 |   11
+(4 rows)
+
 prepare transaction 'pt_1';
 select gid from pg_prepared_xacts where gid = 'pt_1';
- gid 
------
-(0 rows)
+ gid  
+------
+ pt_1
+(1 row)
 
 select is_prepared_on_node('pt_1', 1); -- true
  is_prepared_on_node 
@@ -1708,7 +1831,6 @@ select is_prepared_on_node('pt_1', 1); -- true
 (1 row)
 
 commit prepared 'pt_1';
-ERROR:  prepared transaction with identifier "pt_1" does not exist
 -- ****  
 begin;
   select * from p1 order by 1 for update;
index ebeed8a1df9f226d0c2fe15efba733674656e665..e14db8cfbb893668ba1fad55c3f51559d354f6bf 100644 (file)
@@ -57,6 +57,13 @@ explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2 for share;
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2 for share of t2;
 
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 join t2 on (t1.val = t2.val) for share;
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 left join t2 on (t1.val = t2.val) for share;
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 right join t2 on (t1.val = t2.val) for share;
+select * from t1 join t2 on (t1.val = t2.val) for share;
+select * from t1 left join t2 on (t1.val = t2.val) for share;
+select * from t1 right join t2 on (t1.val = t2.val) for share;
+
 -- three table case
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2, t3;
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2, t3 for update;
@@ -65,6 +72,15 @@ explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2,
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2, t3 for update of t1,t3 nowait;
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1, t2, t3 for share of t1,t2 nowait;
 
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2);
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update;
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update of t1;
+explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update of t1,t3;
+select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2);
+select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update;
+select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update of t1;
+select * from t1 join t2 on (t1.val2 = t2.val2) join t3 on (t1.val2 = t3.val2) for update of t1,t3;
+
 -- check a few subquery cases
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from (select * from t1 for update of t1 nowait) as foo;
 explain (costs off, num_nodes off, nodes off, verbose on)  select * from t1 where val in (select val from t2 for update of t2 nowait) for update;