Repository: tobymao/sqlglot

Issue:
[Postgres 14.4] Parser does not understand inline join in from clause
Example (valid sql):

```
select * from (table1 left join table2 on 1 = 1);
```
Result:
```
ParseError: Expecting ). Line 1, Col: 23.
  select * from (table1 left join table2 on 1 = 1);

Invalid expression / Unexpected token. Line 1, Col: 48.
  select * from (table1 left join table2 on 1 = 1);
```


Discussion:
Is this syntax documented anywhere? I don't see it [here](https://www.postgresql.org/docs/current/sql-select.html).


I believe it's called a "join construct"
https://www.postgresql.org/docs/current/queries-table-expressions.html joined tables
MySQL join clause syntax can also derive this form: https://dev.mysql.com/doc/refman/8.0/en/join.html



Resolving patch:
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -885,10 +885,6 @@ class Reference(Expression):
     arg_types = {"this": True, "expressions": True}
 
 
-class Table(Expression):
-    arg_types = {"this": True, "db": False, "catalog": False}
-
-
 class Tuple(Expression):
     arg_types = {"expressions": False}
 
@@ -998,6 +994,10 @@ def with_(
 }
 
 
+class Table(Expression):
+    arg_types = {"this": True, "db": False, "catalog": False, **QUERY_MODIFIERS}
+
+
 class Union(Subqueryable, Expression):
     arg_types = {
         "with": False,
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -531,7 +531,7 @@ def introducer_sql(self, expression):
         return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
 
     def table_sql(self, expression):
-        return ".".join(
+        table = ".".join(
             part
             for part in [
                 self.sql(expression, "catalog"),
@@ -541,6 +541,8 @@ def table_sql(self, expression):
             if part
         )
 
+        return f"{table}{self.query_modifiers(expression)}"
+
     def tablesample_sql(self, expression):
         if self.alias_post_tablesample and isinstance(expression.this, exp.Alias):
             this = self.sql(expression.this, "this")
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -1032,7 +1032,7 @@ def _parse_subquery(self, this):
         return self.expression(exp.Subquery, this=this, alias=self._parse_table_alias())
 
     def _parse_query_modifiers(self, this):
-        if not isinstance(this, (exp.Subquery, exp.Subqueryable)):
+        if not isinstance(this, (exp.Subquery, exp.Subqueryable, exp.Table)):
             return
 
         for key, parser in self.QUERY_MODIFIER_PARSERS.items():


Test patch:
diff --git a/tests/dialects/test_dialect.py b/tests/dialects/test_dialect.py
--- a/tests/dialects/test_dialect.py
+++ b/tests/dialects/test_dialect.py
@@ -631,6 +631,33 @@ def test_cross_join(self):
             },
         )
 
+    # https://dev.mysql.com/doc/refman/8.0/en/join.html
+    # https://www.postgresql.org/docs/current/queries-table-expressions.html
+    def test_joined_tables(self):
+        self.validate_identity("SELECT * FROM (tbl1 LEFT JOIN tbl2 ON 1 = 1)")
+        self.validate_identity("SELECT * FROM (tbl1 JOIN tbl2 JOIN tbl3)")
+        self.validate_identity(
+            "SELECT * FROM (tbl1 JOIN (tbl2 JOIN tbl3) ON bla = foo)"
+        )
+        self.validate_identity(
+            "SELECT * FROM (tbl1 JOIN LATERAL (SELECT * FROM bla) AS tbl)"
+        )
+
+        self.validate_all(
+            "SELECT * FROM (tbl1 LEFT JOIN tbl2 ON 1 = 1)",
+            write={
+                "postgres": "SELECT * FROM (tbl1 LEFT JOIN tbl2 ON 1 = 1)",
+                "mysql": "SELECT * FROM (tbl1 LEFT JOIN tbl2 ON 1 = 1)",
+            },
+        )
+        self.validate_all(
+            "SELECT * FROM (tbl1 JOIN LATERAL (SELECT * FROM bla) AS tbl)",
+            write={
+                "postgres": "SELECT * FROM (tbl1 JOIN LATERAL (SELECT * FROM bla) AS tbl)",
+                "mysql": "SELECT * FROM (tbl1 JOIN LATERAL (SELECT * FROM bla) AS tbl)",
+            },
+        )
+
     def test_lateral_subquery(self):
         self.validate_identity(
             "SELECT art FROM tbl1 INNER JOIN LATERAL (SELECT art FROM tbl2) AS tbl2 ON tbl1.art = tbl2.art"