Repository: tobymao/sqlglot
Issue category: Feature Request

Update with join seems to be unsupported
I've checked against the latest version of SQLGlot (23.12.2) and can confirm that what I'm trying to do still doesn't work.
I'm trying to create an update dynamically with a join and I'm starting to wonder if update is missing a join argument.
I've tried to construct the expression that I need directly but it's getting a bit complex.
It could be that I'm just missing some documentation on how to handle this.
My specific example is using the TSQL dialect, I do not know if the update syntax differs across SQL Engines, I've tried to keep the example as agnostic as possible.

It seems that SQLGlot is capable of understanding such queries e.g.
```python
from sqlglot import parse_one

raw_sql = """
UPDATE
    Table_A
SET
    Table_A.col1 = Table_B.col1,
    Table_A.col2 = Table_B.col2
FROM
    Some_Table AS Table_A
    INNER JOIN Other_Table AS Table_B
        ON Table_A.id = Table_B.id
WHERE
    Table_A.col3 = 'cool'
"""
parsed_sql = parse_one(raw_sql)
```

But I'm not sure how to get the same output when building the same statement programatically.
This was my first attempt, expecting I might be able to put it in to the `from_` argument.

```python
from sqlglot.expressions import update

update_properties = {
    "Table_A.col1": "Table_B.col1",
    "Table_A.col2": "Table_B.col1",
}
update(
    "Table_A",
    update_properties,
    "Table_A.col3 = 'cool'",
    "Some_Table AS Table_A INNER JOIN Other_Table AS Table_B ON Table_A.id = Table_B.id"
)
```
this gives me a ParseError as I guess it can't handle the join.

I then tried to use parse_one with just the from and join parts to get the expression I needed but realised it comes out as a select statement and the AST for the Table expression is different.
The Table expression has joins when parsing the update statement in full but not when trying to parse_one.

parse_one for FROM and JOIN
```
frozenset({('expressions', (Star(),)), ('from', From(
  this=Table(
    this=Identifier(this=Some_Table, quoted=False),
    alias=TableAlias(
      this=Identifier(this=Table_A, quoted=False))))), ('joins', (Join(
  this=Table(
    this=Identifier(this=Other_Table, quoted=False),
    alias=TableAlias(
      this=Identifier(this=Table_B, quoted=False))),
  kind=INNER,
  on=EQ(
    this=Column(
      this=Identifier(this=id, quoted=False),
      table=Identifier(this=Table_A, quoted=False)),
    expression=Column(
      this=Identifier(this=id, quoted=False),
      table=Identifier(this=Table_B, quoted=False)))),))})
```

from part of parse_one for full update statement
```
From(
  this=Table(
    this=Identifier(this=Some_Table, quoted=False),
    alias=TableAlias(
      this=Identifier(this=Table_A, quoted=False)),
    joins=[
      Join(
        this=Table(
          this=Identifier(this=Other_Table, quoted=False),
          alias=TableAlias(
            this=Identifier(this=Table_B, quoted=False))),
        kind=INNER,
        on=EQ(
          this=Column(
            this=Identifier(this=id, quoted=False),
            table=Identifier(this=Table_A, quoted=False)),
          expression=Column(
            this=Identifier(this=id, quoted=False),
            table=Identifier(this=Table_B, quoted=False))))]))
```

Many thanks for your help in advance!


Functions to edit:
sqlglot/parser.py:Parser
Functions added: sqlglot/parser.py:Parser

Patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index b3e0bfc2be..911b05af4e 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -598,7 +598,7 @@ class Parser(metaclass=_Parser):
         exp.Condition: lambda self: self._parse_conjunction(),
         exp.DataType: lambda self: self._parse_types(allow_identifiers=False),
         exp.Expression: lambda self: self._parse_expression(),
-        exp.From: lambda self: self._parse_from(),
+        exp.From: lambda self: self._parse_from(joins=True),
         exp.Group: lambda self: self._parse_group(),
         exp.Having: lambda self: self._parse_having(),
         exp.Identifier: lambda self: self._parse_id_var(),

Test patch: diff --git a/tests/test_build.py b/tests/test_build.py
index ad0bb9aabe..da1677f50c 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -545,6 +545,10 @@ def test_build(self):
                 lambda: exp.update("tbl", {"x": 1}, from_="tbl2"),
                 "UPDATE tbl SET x = 1 FROM tbl2",
             ),
+            (
+                lambda: exp.update("tbl", {"x": 1}, from_="tbl2 cross join tbl3"),
+                "UPDATE tbl SET x = 1 FROM tbl2 CROSS JOIN tbl3",
+            ),
             (
                 lambda: union("SELECT * FROM foo", "SELECT * FROM bla"),
                 "SELECT * FROM foo UNION SELECT * FROM bla",