Repository: tobymao/sqlglot

Issue:
Constraint Parsing Consuming Tables
```sql
CREATE TABLE t (title text)
```

tries to parse a constraint.

i think the 

```
    def _parse_schema(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
        index = self._index
        if not self._match(TokenType.L_PAREN) or self._match(TokenType.SELECT):
            self._retreat(index)
            return this

        args = self._parse_csv(
            lambda: self._parse_constraint()
            or self._parse_column_def(self._parse_field(any_token=True))
        )
```

the parse_constraint that happens before column_def should only be a couple of common things like primary key, foreign key, like etc.

https://github.com/tobymao/sqlglot/pull/1164


Discussion:
I'll take a look soon.

Resolving patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -616,6 +616,8 @@ class Parser(metaclass=_Parser):
         "UPPERCASE": lambda self: self.expression(exp.UppercaseColumnConstraint),
     }
 
+    SCHEMA_UNNAMED_CONSTRAINTS = {"CHECK", "FOREIGN KEY", "LIKE", "PRIMARY KEY", "UNIQUE"}
+
     NO_PAREN_FUNCTION_PARSERS = {
         TokenType.CASE: lambda self: self._parse_case(),
         TokenType.IF: lambda self: self._parse_if(),
@@ -2933,7 +2935,7 @@ def _parse_column_constraint(self) -> t.Optional[exp.Expression]:
 
     def _parse_constraint(self) -> t.Optional[exp.Expression]:
         if not self._match(TokenType.CONSTRAINT):
-            return self._parse_unnamed_constraint()
+            return self._parse_unnamed_constraint(constraints=self.SCHEMA_UNNAMED_CONSTRAINTS)
 
         this = self._parse_id_var()
         expressions = []
@@ -2946,10 +2948,17 @@ def _parse_constraint(self) -> t.Optional[exp.Expression]:
 
         return self.expression(exp.Constraint, this=this, expressions=expressions)
 
-    def _parse_unnamed_constraint(self) -> t.Optional[exp.Expression]:
-        if not self._match_texts(self.CONSTRAINT_PARSERS):
+    def _parse_unnamed_constraint(
+        self, constraints: t.Optional[t.Collection[str]] = None
+    ) -> t.Optional[exp.Expression]:
+        if not self._match_texts(constraints or self.CONSTRAINT_PARSERS):
             return None
-        return self.CONSTRAINT_PARSERS[self._prev.text.upper()](self)
+
+        constraint = self._prev.text.upper()
+        if constraint not in self.CONSTRAINT_PARSERS:
+            self.raise_error(f"No parser found for schema constraint {constraint}.")
+
+        return self.CONSTRAINT_PARSERS[constraint](self)
 
     def _parse_unique(self) -> exp.Expression:
         if not self._match(TokenType.L_PAREN, advance=False):


Test patch:
diff --git a/tests/fixtures/identity.sql b/tests/fixtures/identity.sql
--- a/tests/fixtures/identity.sql
+++ b/tests/fixtures/identity.sql
@@ -548,6 +548,7 @@ CREATE TABLE customer (pk BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (CYCLE))
 CREATE TABLE foo (baz_id INT REFERENCES baz(id) DEFERRABLE)
 CREATE TABLE foo (baz CHAR(4) CHARACTER SET LATIN UPPERCASE NOT CASESPECIFIC)
 CREATE TABLE foo (baz DATE FORMAT 'YYYY/MM/DD' TITLE 'title')
+CREATE TABLE t (title TEXT)
 CREATE TABLE a, FALLBACK, LOG, JOURNAL, CHECKSUM=DEFAULT, DEFAULT MERGEBLOCKRATIO, BLOCKCOMPRESSION=MANUAL (a INT)
 CREATE TABLE a, NO FALLBACK PROTECTION, NO LOG, NO JOURNAL, CHECKSUM=ON, NO MERGEBLOCKRATIO, BLOCKCOMPRESSION=ALWAYS (a INT)
 CREATE TABLE a, WITH JOURNAL TABLE=x.y.z, CHECKSUM=OFF, MERGEBLOCKRATIO=1, DATABLOCKSIZE=10 KBYTES (a INT)