Repository: ibis-project/ibis

Issue:
bug: Snowflake insert is not working even for a very simple table
### What happened?

I have following ibis code for snowflake to reproduce the issue
```
import pandas
import ib is
df = pandas.DataFrame.from_dict({"name": ['n1']})
ibis_con = ibis.snowflake.connect(...)
ibis_con.create_table("TEST", schema=ibis.schema([("name", "string")]), overwrite=True)
ibis_con.insert('TEST', df) --> throw exception sqlalchemy.exc.ProgrammingError: (snowflake.connector.errors.ProgrammingError) 001003 (42000): SQL compilation error

# create table with the same df works
ibis_con.create_table("TEST", df, overwrite=True)
```


### What version of ibis are you using?

7.2.0

### What backend(s) are you using, if any?

snowflake

### Relevant log output

_No response_

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct


Discussion:
hi @ruiyang2015, thanks for opening! as a workaround, can you try inserting as an Ibis table instead of a pandas dataframe? and provide more details on the exception?

I was able to insert an Ibis table, but do receive an error about `NAN` values for pandas -- example here: https://gist.github.com/lostmygithubaccount/6535ff05c2f80c8b967fcb6039db05e4 

Resolving patch:
diff --git a/ibis/backends/snowflake/__init__.py b/ibis/backends/snowflake/__init__.py
--- a/ibis/backends/snowflake/__init__.py
+++ b/ibis/backends/snowflake/__init__.py
@@ -973,6 +973,45 @@ def read_parquet(
 
         return self.table(table)
 
+    def insert(
+        self,
+        table_name: str,
+        obj: pd.DataFrame | ir.Table | list | dict,
+        schema: str | None = None,
+        database: str | None = None,
+        overwrite: bool = False,
+    ) -> None:
+        """Insert data into a table.
+
+        Parameters
+        ----------
+        table_name
+            The name of the table to which data needs will be inserted
+        obj
+            The source data or expression to insert
+        schema
+            The name of the schema that the table is located in
+        database
+            Name of the attached database that the table is located in.
+        overwrite
+            If `True` then replace existing contents of table
+        """
+        if not isinstance(obj, ir.Table):
+            obj = ibis.memtable(obj)
+
+        self._run_pre_execute_hooks(obj)
+        query = sg.exp.insert(
+            expression=self.compile(obj),
+            into=sg.table(table_name, db=schema, catalog=database, quoted=True),
+            columns=[sg.column(col, quoted=True) for col in obj.columns],
+            dialect=self.name,
+        )
+        with self.begin() as con:
+            if overwrite:
+                con.exec_driver_sql(f"TRUNCATE TABLE {query.into.sql(self.name)}")
+
+            con.exec_driver_sql(query.sql(self.name))
+
 
 @compiles(sa.sql.Join, "snowflake")
 def compile_join(element, compiler, **kw):


Test patch:
diff --git a/ibis/backends/snowflake/tests/test_client.py b/ibis/backends/snowflake/tests/test_client.py
--- a/ibis/backends/snowflake/tests/test_client.py
+++ b/ibis/backends/snowflake/tests/test_client.py
@@ -252,3 +252,26 @@ def test_array_repr(con, monkeypatch):
     t = con.tables.ARRAY_TYPES
     expr = t.x
     assert repr(expr)
+
+
+def test_insert(con):
+    name = gen_name("test_insert")
+
+    t = con.create_table(
+        name, schema=ibis.schema({"a": "int", "b": "string", "c": "int"}), temp=True
+    )
+    assert t.count().execute() == 0
+
+    expected = pd.DataFrame({"a": [1, 2, 3], "b": ["x", "y", None], "c": [2, None, 3]})
+
+    con.insert(name, ibis.memtable(expected))
+
+    result = t.order_by("a").execute()
+
+    tm.assert_frame_equal(result, expected)
+
+    con.insert(name, expected)
+    assert t.count().execute() == 6
+
+    con.insert(name, expected, overwrite=True)
+    assert t.count().execute() == 3