diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result
index b2a88073cce429279a973700f7ce581343ade554..432a6c3e52be1cff188164aa7f949308aa11caed 100644
--- a/mysql-test/r/select.result
+++ b/mysql-test/r/select.result
@@ -2446,3 +2446,12 @@ cast((a - b) as unsigned)
 1
 18446744073709551615
 drop table t1;
+create table t1 (a int, b int);
+create table t2 like t1;
+select t1.a from (t1 inner join t2 on t1.a=t2.a) where t2.a=1;
+a
+select t1.a from ((t1 inner join t2 on t1.a=t2.a)) where t2.a=1;
+a
+select x.a, y.a, z.a from ( (t1 x inner join t2 y on x.a=y.a) inner join t2 z on y.a=z.a) WHERE x.a=1;
+a	a	a
+drop table t1,t2;
diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test
index 376a23c06e5598c9e968e2fa8074713432f0c733..ae80059091777bd4813dfd986db02542a06d5d1b 100644
--- a/mysql-test/t/select.test
+++ b/mysql-test/t/select.test
@@ -2015,3 +2015,13 @@ select a-b , (a-b < 0)  from t1 order by 1;
 select a-b as d, (a-b >= 0), b from t1 group by b having d >= 0;
 select cast((a - b) as unsigned) from t1 order by 1;
 drop table t1;
+
+#
+# Bug#8670
+#
+create table t1 (a int, b int);
+create table t2 like t1;
+select t1.a from (t1 inner join t2 on t1.a=t2.a) where t2.a=1;
+select t1.a from ((t1 inner join t2 on t1.a=t2.a)) where t2.a=1;
+select x.a, y.a, z.a from ( (t1 x inner join t2 y on x.a=y.a) inner join t2 z on y.a=z.a) WHERE x.a=1;
+drop table t1,t2;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ca8414f9d54bb04ee5e6f96a7ae05c31a0af5097..d39ceef396c6451b504df3a18f3d273a1be5f93a 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5850,6 +5850,7 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
 {
   TABLE_LIST *ptr;
   DBUG_ENTER("end_nested_join");
+  DBUG_ASSERT(embedding);
   ptr= embedding;
   join_list= ptr->join_list;
   embedding= ptr->embedding;
@@ -5863,6 +5864,12 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
     join_list->push_front(embedded);
     ptr= embedded;
   }
+  else
+  if (nested_join->join_list.elements == 0)
+  {
+    join_list->pop();
+    DBUG_RETURN(0);
+  }
   DBUG_RETURN(ptr);
 }
 
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index ef5cbb3c79bd7f06a948ff8aec05f20ceb35c6a0..5c0ec8b417daf32590a9ee832b85b29b42921aaa 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -52,6 +52,13 @@ const LEX_STRING null_lex_str={0,0};
 		      ER_WARN_DEPRECATED_SYNTAX, \
 		      ER(ER_WARN_DEPRECATED_SYNTAX), (A), (B));
 
+#define TEST_ASSERT(A) \
+  if (!(A)) \
+  {					\
+    yyerror(ER(ER_SYNTAX_ERROR));	\
+    YYABORT;				\
+  }
+
 /* Helper for parsing "IS [NOT] truth_value" */
 inline Item *is_truth_value(Item *A, bool v1, bool v2)
 {
@@ -692,6 +699,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
         opt_var_ident_type delete_option opt_temporary all_or_any opt_distinct
         opt_ignore_leaves fulltext_options spatial_type union_option
         start_transaction_opts opt_chain opt_release
+        union_opt select_derived_init
 
 %type <ulong_num>
 	ULONG_NUM raid_types merge_insert_types
@@ -738,6 +746,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
 %type <table_list>
 	join_table_list  join_table
         table_factor table_ref
+        select_derived derived_table_list
 
 %type <date_time_type> date_time_type;
 %type <interval> interval
@@ -772,6 +781,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
 %type <variable> internal_variable_name
 
 %type <select_lex> in_subselect in_subselect_init
+	get_select_lex
 
 %type <boolfunc2creator> comp_op
 
@@ -4024,13 +4034,53 @@ optional_braces:
 
 /* all possible expressions */
 expr:	
-	expr or bool_term	{ $$= new Item_cond_or($1,$3); }
-	| expr XOR bool_term	{ $$= new Item_cond_xor($1,$3); }
-	| bool_term ;
+	  bool_term { Select->expr_list.push_front(new List<Item>); }
+          bool_or_expr
+          {
+            List<Item> *list= Select->expr_list.pop();
+            if (list->elements)
+            {
+              list->push_front($1);
+              $$= new Item_cond_or(*list);
+              /* optimize construction of logical OR to reduce
+                 amount of objects for complex expressions */
+            }
+            else
+              $$= $1;
+            delete list;
+          }
+	;
+
+bool_or_expr:
+	/* empty */
+        | bool_or_expr or bool_term
+          { Select->expr_list.head()->push_back($3); }
+        ;
 
 bool_term:
-	bool_term and bool_factor { $$= new Item_cond_and($1,$3); }
-	| bool_factor ;
+	bool_term XOR bool_term { $$= new Item_cond_xor($1,$3); }
+	| bool_factor { Select->expr_list.push_front(new List<Item>); }
+          bool_and_expr
+          {
+            List<Item> *list= Select->expr_list.pop();
+            if (list->elements)
+            {
+              list->push_front($1);
+              $$= new Item_cond_and(*list);
+              /* optimize construction of logical AND to reduce
+                 amount of objects for complex expressions */
+            }
+            else
+              $$= $1;
+            delete list;
+          }
+	;
+
+bool_and_expr:
+	/* empty */
+        | bool_and_expr and bool_factor
+          { Select->expr_list.head()->push_back($3); }
+        ;
 
 bool_factor:
 	NOT_SYM bool_factor	{ $$= negate_expression(YYTHD, $2); }
@@ -4915,6 +4965,7 @@ when_list2:
 	    sel->when_list.head()->push_back($5);
 	  };
 
+/* Warning - may return NULL in case of incomplete SELECT */
 table_ref:
         table_factor            { $$=$1; }
         | join_table            { $$=$1; }
@@ -4926,36 +4977,47 @@ table_ref:
         ;
 
 join_table_list:
+	derived_table_list		{ TEST_ASSERT($$=$1); }
+	;
+        
+/* Warning - may return NULL in case of incomplete SELECT */
+derived_table_list:
         table_ref { $$=$1; }
-        | join_table_list ',' table_ref { $$=$3; }
+        | derived_table_list ',' table_ref
+          {
+            TEST_ASSERT($1 && ($$=$3));
+          }
         ;
 
 join_table:
-        table_ref normal_join table_ref { $$=$3; }
+        table_ref normal_join table_ref { TEST_ASSERT($1 && ($$=$3)); }
 	| table_ref STRAIGHT_JOIN table_factor
-	  { $3->straight=1; $$=$3 ; }
+	  {  TEST_ASSERT($1 && ($$=$3)); $3->straight=1; }
 	| table_ref normal_join table_ref ON expr
-	  { add_join_on($3,$5); $$=$3; }
+	  { TEST_ASSERT($1 && ($$=$3)); add_join_on($3,$5); }
 	| table_ref normal_join table_ref
 	  USING
 	  {
 	    SELECT_LEX *sel= Select;
+            TEST_ASSERT($1 && $3);
             sel->save_names_for_using_list($1, $3);
 	  }
 	  '(' using_list ')'
 	  { add_join_on($3,$7); $$=$3; }
 
 	| table_ref LEFT opt_outer JOIN_SYM table_ref ON expr
-	  { add_join_on($5,$7); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
+	  { TEST_ASSERT($1 && $5); add_join_on($5,$7); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
 	| table_ref LEFT opt_outer JOIN_SYM table_factor
 	  {
 	    SELECT_LEX *sel= Select;
+            TEST_ASSERT($1 && $5);
             sel->save_names_for_using_list($1, $5);
 	  }
 	  USING '(' using_list ')'
 	  { add_join_on($5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
 	| table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
 	  {
+            TEST_ASSERT($1 && $6);
 	    add_join_natural($1,$6);
 	    $6->outer_join|=JOIN_TYPE_LEFT;
 	    $$=$6;
@@ -4963,6 +5025,7 @@ join_table:
 	| table_ref RIGHT opt_outer JOIN_SYM table_ref ON expr
           { 
 	    LEX *lex= Lex;
+            TEST_ASSERT($1 && $5);
             if (!($$= lex->current_select->convert_right_join()))
               YYABORT;
             add_join_on($$, $7);
@@ -4970,6 +5033,7 @@ join_table:
 	| table_ref RIGHT opt_outer JOIN_SYM table_factor
 	  {
 	    SELECT_LEX *sel= Select;
+            TEST_ASSERT($1 && $5);
             sel->save_names_for_using_list($1, $5);
 	  }
 	  USING '(' using_list ')'
@@ -4981,13 +5045,14 @@ join_table:
           }
 	| table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
 	  {
+            TEST_ASSERT($1 && $6);
 	    add_join_natural($6,$1);
 	    LEX *lex= Lex;
             if (!($$= lex->current_select->convert_right_join()))
               YYABORT;
 	  }
 	| table_ref NATURAL JOIN_SYM table_factor
-	  { add_join_natural($1,$4); $$=$4; };
+	  { TEST_ASSERT($1 && ($$=$4)); add_join_natural($1,$4); };
         
 
 normal_join:
@@ -4996,6 +5061,7 @@ normal_join:
 	| CROSS JOIN_SYM	{}
 	;
 
+/* Warning - may return NULL in case of incomplete SELECT */
 table_factor:
 	{
 	  SELECT_LEX *sel= Select;
@@ -5014,50 +5080,96 @@ table_factor:
 	    YYABORT;
           sel->add_joined_table($$);
 	}
-        | '('
+	| '{' ident table_ref LEFT OUTER JOIN_SYM table_ref ON expr '}'
+	  { TEST_ASSERT($3 && $7); add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }
+	| select_derived_init get_select_lex select_derived2
           {
             LEX *lex= Lex;
-            if (lex->current_select->init_nested_join(lex->thd))
-              YYABORT;
+            SELECT_LEX *sel= lex->current_select;
+            if ($1)
+            {
+	      if (sel->set_braces(1))
+	      {
+	        yyerror(ER(ER_SYNTAX_ERROR));
+	        YYABORT;
+	      }
+              /* select in braces, can't contain global parameters */
+	      if (sel->master_unit()->fake_select_lex)
+                sel->master_unit()->global_parameters=
+                   sel->master_unit()->fake_select_lex;
+            }
+            if ($2->init_nested_join(lex->thd))
+              YYABORT;            
+            $$= 0;
+            /* incomplete derived tables return NULL, we must be 
+               nested in select_derived rule to be here. */
           }
-          join_table_list ')'
+	| '(' get_select_lex select_derived union_opt ')' opt_table_alias
+	{
+          /* Use $2 instead of Lex->current_select as derived table will
+             alter value of Lex->current_select. */
+
+          if (!($3 || $6) && $2->embedding && 
+              !$2->embedding->nested_join->join_list.elements)
           {
-            LEX *lex= Lex;
-            if (!($$= lex->current_select->end_nested_join(lex->thd)))
-              YYABORT;
+            /* we have a derived table ($3 == NULL) but no alias,
+               Since we are nested in further parentheses so we
+               can pass NULL to the outer level parentheses
+               Permits parsing of "((((select ...))) as xyz)" */
+            $$= 0;
           }
-	| '{' ident table_ref LEFT OUTER JOIN_SYM table_ref ON expr '}'
-	  { add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }
-	| '(' select_derived union_opt ')' opt_table_alias
-	{
-	  LEX *lex=Lex;
-	  SELECT_LEX_UNIT *unit= lex->current_select->master_unit();
-	  lex->current_select= unit->outer_select();
-	  if (!($$= lex->current_select->
-                add_table_to_list(lex->thd, new Table_ident(unit), $5, 0,
-				  TL_READ,(List<String> *)0,
-	                          (List<String> *)0)))
+          else
+          if (!$3)
+          {
+            /* Handle case of derived table, alias may be NULL if there
+               are no outer parentheses, add_table_to_list() will throw
+               error in this case */
+	    LEX *lex=Lex;
+            SELECT_LEX *sel= lex->current_select;
+	    SELECT_LEX_UNIT *unit= sel->master_unit();
+	    lex->current_select= sel= unit->outer_select();
+	    if (!($$= sel->
+                  add_table_to_list(lex->thd, new Table_ident(unit), $6, 0,
+				    TL_READ,(List<String> *)0,
+	                            (List<String> *)0)))
 
+	      YYABORT;
+            sel->add_joined_table($$);           
+          }
+	  else
+          if ($4 || $6)
+	  {
+            /* simple nested joins cannot have aliases or unions */
+            yyerror(ER(ER_SYNTAX_ERROR));
 	    YYABORT;
-          lex->current_select->add_joined_table($$);           
-	};
-
+	  }
+          else
+            $$= $3;
+	}
+        ;
 
+/* handle contents of parentheses in join expression */
 select_derived:
-	SELECT_SYM select_derived2
-	| '(' select_derived ')'
+	  get_select_lex
 	  {
-            SELECT_LEX *sel= Select;
-	    if (sel->set_braces(1))
-	    {
+            LEX *lex= Lex;
+            if ($1->init_nested_join(lex->thd))
+              YYABORT;
+          }
+          derived_table_list
+          {
+            LEX *lex= Lex;
+            /* for normal joins, $3 != NULL and end_nested_join() != NULL,
+               for derived tables, both must equal NULL */
+            
+            if (!($$= $1->end_nested_join(lex->thd)) && $3)
+              YYABORT;
+            if (!$3 && $$)
+            {
 	      yyerror(ER(ER_SYNTAX_ERROR));
 	      YYABORT;
-	    }
-            /* select in braces, can't contain global parameters */
-	    if (sel->master_unit()->fake_select_lex)
-              sel->master_unit()->global_parameters=
-                 sel->master_unit()->fake_select_lex;
-	  }
+            }
+          }
  	;
 
 select_derived2:
@@ -5085,6 +5197,29 @@ select_derived2:
 	opt_select_from
         ;
 
+get_select_lex:
+	/* Empty */ { $$= Select; }
+        ;
+
+select_derived_init:
+          SELECT_SYM
+          {
+            LEX *lex= Lex;
+            SELECT_LEX *sel= lex->current_select;        
+            TABLE_LIST *embedding;
+            if (!sel->embedding || sel->end_nested_join(lex->thd))
+	    {
+              /* we are not in parentheses */
+              yyerror(ER(ER_SYNTAX_ERROR));
+	      YYABORT;
+	    }
+            embedding= Select->embedding;
+            $$= embedding &&
+                !embedding->nested_join->join_list.elements;
+            /* return true if we are deeply nested */
+          }
+        ;
+
 opt_outer:
 	/* empty */	{}
 	| OUTER		{};
@@ -8118,13 +8253,12 @@ union_list:
 	;
 
 union_opt:
-	union_list {}
-	| optional_order_or_limit {}
+	/* Empty */ { $$= 0; }
+	| union_list { $$= 1; }
+	| union_order_or_limit { $$= 1; }
 	;
 
-optional_order_or_limit:
-	/* Empty */ {}
-	|
+union_order_or_limit:
 	  {
 	    THD *thd= YYTHD;
 	    LEX *lex= thd->lex;