Repository: tobymao/sqlglot

Issue:
Parsing issue for Snowflake COPY INTO with subqueries and FILE_FORMAT
Hi!

This issue sounds like an extension of previously reported: https://github.com/tobymao/sqlglot/issues/3434 where support for COPY INTO with subqueries was added.

Looks like the library is facing an issue when FILE_FORMAT is being added to the query:
```
from sqlglot import parse_one

query = "COPY INTO @my_stage/result/data FROM (SELECT * FROM orderstiny) file_format=(type='csv');"
parse_one(query, read='snowflake')
```
above is producing following error:

```
sqlglot.errors.ParseError: Required keyword: 'this' missing for <class 'sqlglot.expressions.CopyParameter'>. Line 1, Col: 77
```

Please let me know in case of any additional information is needed.

Discussion:

Patch:
diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py
index ec32a92c..8c3c471c 100644
--- a/sqlglot/dialects/snowflake.py
+++ b/sqlglot/dialects/snowflake.py
@@ -714,7 +714,7 @@ class Snowflake(Dialect):
         def _parse_file_location(self) -> t.Optional[exp.Expression]:
             # Parse either a subquery or a staged file
             return (
-                self._parse_select(table=True)
+                self._parse_select(table=True, parse_subquery_alias=False)
                 if self._match(TokenType.L_PAREN, advance=False)
                 else self._parse_table_parts()
             )
diff --git a/sqlglot/dialects/tsql.py b/sqlglot/dialects/tsql.py
index ee403a71..ab49d08c 100644
--- a/sqlglot/dialects/tsql.py
+++ b/sqlglot/dialects/tsql.py
@@ -994,9 +994,13 @@ class TSQL(Dialect):
                 elif kind == "TABLE":
                     assert table
                     where = exp.and_(
-                        exp.column("table_name").eq(table.name),
-                        exp.column("table_schema").eq(table.db) if table.db else None,
-                        exp.column("table_catalog").eq(table.catalog) if table.catalog else None,
+                        exp.column("table_name").eq(exp.Literal.string(table.name)),
+                        exp.column("table_schema").eq(exp.Literal.string(table.db))
+                        if table.db
+                        else None,
+                        exp.column("table_catalog").eq(exp.Literal.string(table.catalog))
+                        if table.catalog
+                        else None,
                     )
                     sql = f"""IF NOT EXISTS (SELECT * FROM information_schema.tables WHERE {where}) EXEC({sql})"""
                 elif kind == "INDEX":
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
index a74b7521..84133a3f 100644
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -832,7 +832,7 @@ class Expression(metaclass=_Expression):
 
     def _binop(self, klass: t.Type[E], other: t.Any, reverse: bool = False) -> E:
         this = self.copy()
-        other = convert(other, copy=True)
+        other = maybe_parse(other, copy=True)
         if not isinstance(this, klass) and not isinstance(other, klass):
             this = _wrap(this, Binary)
             other = _wrap(other, Binary)


Test patch:
diff --git a/tests/dialects/test_snowflake.py b/tests/dialects/test_snowflake.py
index ba857191..9d9371db 100644
--- a/tests/dialects/test_snowflake.py
+++ b/tests/dialects/test_snowflake.py
@@ -1934,6 +1934,9 @@ STORAGE_ALLOWED_LOCATIONS=('s3://mybucket1/path1/', 's3://mybucket2/path2/')""",
         self.validate_identity(
             """COPY INTO mytable (col1, col2) FROM 's3://mybucket/data/files' STORAGE_INTEGRATION = "storage" ENCRYPTION = (TYPE='NONE' MASTER_KEY='key') FILES = ('file1', 'file2') PATTERN = 'pattern' FILE_FORMAT = (FORMAT_NAME=my_csv_format NULL_IF=('')) PARSE_HEADER = TRUE"""
         )
+        self.validate_identity(
+            """COPY INTO @my_stage/result/data FROM (SELECT * FROM orderstiny) FILE_FORMAT = (TYPE='csv')"""
+        )
         self.validate_all(
             """COPY INTO 's3://example/data.csv'
     FROM EXTRA.EXAMPLE.TABLE
diff --git a/tests/test_build.py b/tests/test_build.py
index 150bb42e..523dcc0a 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -67,16 +67,17 @@ class TestBuild(unittest.TestCase):
             (lambda: 1 + x * 2 + 3, "1 + (x * 2) + 3"),
             (lambda: x * 1 * 2 + 3, "(x * 1 * 2) + 3"),
             (lambda: 1 + (x * 2) / 3, "1 + ((x * 2) / 3)"),
-            (lambda: x & "y", "x AND 'y'"),
-            (lambda: x | "y", "x OR 'y'"),
+            (lambda: x & "'y'", "x AND 'y'"),
+            (lambda: x | "'y'", "x OR 'y'"),
             (lambda: -x, "-x"),
             (lambda: ~x, "NOT x"),
             (lambda: x[1], "x[1]"),
             (lambda: x[1, 2], "x[1, 2]"),
             (lambda: x["y"] + 1, "x['y'] + 1"),
-            (lambda: x.like("y"), "x LIKE 'y'"),
-            (lambda: x.ilike("y"), "x ILIKE 'y'"),
-            (lambda: x.rlike("y"), "REGEXP_LIKE(x, 'y')"),
+            (lambda: x.like("y"), "x LIKE y"),
+            (lambda: x.like("'y'"), "x LIKE 'y'"),
+            (lambda: x.ilike("'y'"), "x ILIKE 'y'"),
+            (lambda: x.rlike("'y'"), "REGEXP_LIKE(x, 'y')"),
             (
                 lambda: case().when("x = 1", "x").else_("bar"),
                 "CASE WHEN x = 1 THEN x ELSE bar END",