Repository: tobymao/sqlglot

Issue:
CREATE TABLE - problem parsing the Teradata WITH DATA clause
There is a problem parsing a CREATE TABLE statement in Teradata that has a WITH DATA clause

sqlglot version 17.2.0
Teradata version 16.20

The SQL is 
CREATE VOLATILE SET TABLE example1 AS (
    SELECT col1,col2, col3
    from table1
)
WITH DATA
PRIMARY INDEX (col1)
ON COMMIT PRESERVE ROWS;

and code is 
for statement in sqlglot.parse(sql, read="teradata"):
    for table in statement.find_all(exp.Table):
        print(table.name)

commenting out the with clause will work. Error message is

---------------------------------------------------------------------------
ParseError                                Traceback (most recent call last)
Cell In[21], line 1
----> 1 for statement in sqlglot.parse(raw_sql, read="teradata"):
      2     for table in statement.find_all(exp.Table):
      3         print(table.name)

File ~\AppData\Roaming\Python\Python311\site-packages\sqlglot\__init__.py:83, in parse(sql, read, **opts)
     71 """
     72 Parses the given SQL string into a collection of syntax trees, one per parsed SQL statement.
     73 
   (...)
     80     The resulting syntax tree collection.
     81 """
     82 dialect = Dialect.get_or_raise(read)()
---> 83 return dialect.parse(sql, **opts)

File ~\AppData\Roaming\Python\Python311\site-packages\sqlglot\dialects\dialect.py:278, in Dialect.parse(self, sql, **opts)
    277 def parse(self, sql: str, **opts) -> t.List[t.Optional[exp.Expression]]:
--> 278     return self.parser(**opts).parse(self.tokenize(sql), sql)

File ~\AppData\Roaming\Python\Python311\site-packages\sqlglot\parser.py:877, in Parser.parse(self, raw_tokens, sql)
    863 def parse(
    864     self, raw_tokens: t.List[Token], sql: t.Optional[str] = None
    865 ) -> t.List[t.Optional[exp.Expression]]:
    866     """
    867     Parses a list of tokens and returns a list of syntax trees, one tree
    868     per parsed SQL statement.
   (...)
    875         The list of the produced syntax trees.
    876     """
--> 877     return self._parse(
    878         parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql
    879     )

File ~\AppData\Roaming\Python\Python311\site-packages\sqlglot\parser.py:946, in Parser._parse(self, parse_method, raw_tokens, sql)
    943     expressions.append(parse_method(self))
    945     if self._index < len(self._tokens):
--> 946         self.raise_error("Invalid expression / Unexpected token")
    948     self.check_errors()
    950 return expressions

File ~\AppData\Roaming\Python\Python311\site-packages\sqlglot\parser.py:987, in Parser.raise_error(self, message, token)
    975 error = ParseError.new(
    976     f"{message}. Line {token.line}, Col: {token.col}.\n"
    977     f"  {start_context}\033[4m{highlight}\033[0m{end_context}",
   (...)
    983     end_context=end_context,
    984 )
    986 if self.error_level == ErrorLevel.IMMEDIATE:
--> 987     raise error
    989 self.errors.append(error)

ParseError: Invalid expression / Unexpected token. Line 7, Col: 7.
  
CREATE VOLATILE SET TABLE example1 AS (
    SELECT col1,col2, col3
    from table1
)
WITH DATA
PRIMARY INDEX (col1)
ON COMMIT PRESERVE ROWS;



Discussion:

Patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index 1d57a88b..f5e3c893 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -1234,11 +1234,14 @@ class Parser(metaclass=_Parser):
             expression = self._parse_ddl_select()
 
             if create_token.token_type == TokenType.TABLE:
+                # exp.Properties.Location.POST_EXPRESSION
+                extend_props(self._parse_properties())
+
                 indexes = []
                 while True:
                     index = self._parse_index()
 
-                    # exp.Properties.Location.POST_EXPRESSION and POST_INDEX
+                    # exp.Properties.Location.POST_INDEX
                     extend_props(self._parse_properties())
 
                     if not index:
@@ -1385,7 +1388,6 @@ class Parser(metaclass=_Parser):
     def _parse_with_property(
         self,
     ) -> t.Optional[exp.Expression] | t.List[t.Optional[exp.Expression]]:
-        self._match(TokenType.WITH)
         if self._match(TokenType.L_PAREN, advance=False):
             return self._parse_wrapped_csv(self._parse_property)
 


Test patch:
diff --git a/tests/dialects/test_teradata.py b/tests/dialects/test_teradata.py
index 6906e47d..0df6d0be 100644
--- a/tests/dialects/test_teradata.py
+++ b/tests/dialects/test_teradata.py
@@ -61,6 +61,9 @@ class TestTeradata(Validator):
         self.validate_identity(
             "CREATE VOLATILE MULTISET TABLE a, NOT LOCAL AFTER JOURNAL, FREESPACE=1 PERCENT, DATABLOCKSIZE=10 BYTES, WITH NO CONCURRENT ISOLATED LOADING FOR ALL (a INT)"
         )
+        self.validate_identity(
+            "CREATE VOLATILE SET TABLE example1 AS (SELECT col1, col2, col3 FROM table1) WITH DATA PRIMARY INDEX (col1) ON COMMIT PRESERVE ROWS"
+        )
 
         self.validate_all(
             """