Repository: tobymao/sqlglot

Issue:
Oracle - insert append - error parsing hint
Error in parsing Oracle insert append.

# SQL

```SQL
-- Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
select banner from v$version;
-- create table
create table iap_tbl (id int, col1 varchar2(20 char));
/
-- normal insert
insert into iap_tbl (id, col1) values (1, 'test');
commit;
/
select * from iap_tbl;
/
-- insert with append hint
insert /*+ append */ into iap_tbl (id, col1) values (2, 'test2');
commit;
```

# Python

```Python
# pip show sqlglot
#Name: sqlglot
#Version: 22.2.0

from sqlglot import parse_one

# error parsing hint
sql_nok = "insert /*+ append */ into iap_tbl (id, col1) values (2, 'test2')"

# ok when + removed from comment, normal comment not hint
sql_ok = "insert /* append */ into iap_tbl (id, col1) values (2, 'test2')"

parse_one(sql_ok)

parse_one(sql_nok)
```



Discussion:
@sunrutcon this should be relatively easy to handle, would appreciate if you took the time to contribute a pr, 

in _parse_insert, handle the insert hint
I'm working on it. I'll try to solve this until the end of the week.
Hi @sunrutcon, I've already started looking at this and I expect to have it fixed by tonight, apologies for the late notice. Thanks for reporting this and feel free to report and/or contribute your fixes for any other issues you encounter.

Resolving patch:
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -1828,6 +1828,7 @@ class Index(Expression):
 
 class Insert(DDL, DML):
     arg_types = {
+        "hint": False,
         "with": False,
         "this": True,
         "expression": False,
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -1451,6 +1451,7 @@ def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningP
         return f"{sql})"
 
     def insert_sql(self, expression: exp.Insert) -> str:
+        hint = self.sql(expression, "hint")
         overwrite = expression.args.get("overwrite")
 
         if isinstance(expression.this, exp.Directory):
@@ -1481,7 +1482,7 @@ def insert_sql(self, expression: exp.Insert) -> str:
         else:
             expression_sql = f"{returning}{expression_sql}{on_conflict}"
 
-        sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
+        sql = f"INSERT{hint}{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
         return self.prepend_ctes(expression, sql)
 
     def intersect_sql(self, expression: exp.Intersect) -> str:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -2061,6 +2061,7 @@ def _parse_describe(self) -> exp.Describe:
 
     def _parse_insert(self) -> exp.Insert:
         comments = ensure_list(self._prev_comments)
+        hint = self._parse_hint()
         overwrite = self._match(TokenType.OVERWRITE)
         ignore = self._match(TokenType.IGNORE)
         local = self._match_text_seq("LOCAL")
@@ -2087,6 +2088,7 @@ def _parse_insert(self) -> exp.Insert:
         return self.expression(
             exp.Insert,
             comments=comments,
+            hint=hint,
             this=this,
             by_name=self._match_text_seq("BY", "NAME"),
             exists=self._parse_exists(),


Test patch:
diff --git a/tests/dialects/test_oracle.py b/tests/dialects/test_oracle.py
--- a/tests/dialects/test_oracle.py
+++ b/tests/dialects/test_oracle.py
@@ -210,6 +210,8 @@ def test_hints(self):
         self.validate_identity(
             "SELECT /*+ LEADING(e j) */ * FROM employees e, departments d, job_history j WHERE e.department_id = d.department_id AND e.hire_date = j.start_date"
         )
+        self.validate_identity("INSERT /*+ APPEND */ INTO IAP_TBL (id, col1) VALUES (2, 'test2')")
+        self.validate_identity("INSERT /*+ APPEND_VALUES */ INTO dest_table VALUES (i, 'Value')")
 
     def test_xml_table(self):
         self.validate_identity("XMLTABLE('x')")