Repository: sqlfluff/sqlfluff
Issue category: Feature Request

Snowflake: Create Table does not support In Line Foreign Key Constraints
### Search before asking

- [X] I searched the [issues](https://github.com/sqlfluff/sqlfluff/issues) and found no similar issues.


### What Happened

This report follows on from #6473 

Whilst attempting to `lint` the following DDL

```sql
CREATE OR REPLACE TABLE SOME_TABLE1 (
    ID INTEGER NOT NULL CONSTRAINT MY_FK
    FOREIGN KEY REFERENCES ADDRESSES (ADDRESS)
    ON DELETE CASCADE
);

CREATE OR REPLACE TABLE SOME_TABLE2 (
    ID INTEGER NOT NULL,
    CONSTRAINT MY_FK FOREIGN KEY (ID) REFERENCES ADDRESSES (ADDRESS)
    ON DELETE CASCADE
);
```

The first statement with inline foreign key fails to lint.

```cmd
❯ sqlfluff lint --dialect snowflake create_table.sql
❯ sqlfluff lint --dialect snowflake create_table.sql
== [create_table.sql] FAIL
L:   1 | P:  37 |  PRS | Line 1, Position 37: Found unparsable section: '(\n
                       | ID INTEGER NOT NULL CONSTRAINT MY_...'
WARNING: Parsing errors found and dialect is set to 'snowflake'. Have you configured your dialect correctly?
All Finished 📜 🎉!
```

The second statement lints successfully

### Expected Behaviour

The lint command should run successfully and not report unparseable

### Observed Behaviour

The following error was reported

```cmd
❯ sqlfluff lint --dialect snowflake create_table.sql
== [create_table.sql] FAIL
L:   6 | P:  36 |  PRS | Line 6, Position 36: Found unparsable section: '(\n
                       | ID INTEGER NOT NULL CONSTRAINT MY_...'
WARNING: Parsing errors found and dialect is set to 'snowflake'. Have you configured your dialect correctly?
All Finished 📜 🎉!
```

### How to reproduce

Add the DDL above into `create_table.sql` and execute

```cmd
sqlfluff lint --dialect snowflake create_table.sql
```

### Dialect

snowflake

### Version

3.2.5

### Configuration

files do not exist

### Are you willing to work on and submit a PR to address the issue?

- [X] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/sqlfluff/sqlfluff/blob/main/CODE_OF_CONDUCT.md)



Functions to edit:
src/sqlfluff/dialects/dialect_snowflake.py:AlterTableConstraintActionSegment
src/sqlfluff/dialects/dialect_snowflake.py:ConstraintPropertiesSegment
src/sqlfluff/dialects/dialect_snowflake.py:ColumnConstraintSegment
src/sqlfluff/dialects/dialect_snowflake.py:CreateTableStatementSegment
src/sqlfluff/dialects/dialect_snowflake.py:CreateExternalTableSegment
Functions added: src/sqlfluff/dialects/dialect_snowflake.py:InlineConstraintPropertiesSegment
src/sqlfluff/dialects/dialect_snowflake.py:OutOfLineConstraintPropertiesSegment

Patch:
diff --git a/src/sqlfluff/dialects/dialect_snowflake.py b/src/sqlfluff/dialects/dialect_snowflake.py
index 6f1f12d22c3..59a85e5c980 100644
--- a/src/sqlfluff/dialects/dialect_snowflake.py
+++ b/src/sqlfluff/dialects/dialect_snowflake.py
@@ -2335,7 +2335,7 @@ class AlterTableConstraintActionSegment(BaseSegment):
         # Add Column
         Sequence(
             "ADD",
-            Ref("ConstraintPropertiesSegment"),
+            Ref("OutOfLineConstraintPropertiesSegment"),
         ),
         Sequence(
             "DROP",
@@ -3934,10 +3934,56 @@ class WarehouseObjectParamsSegment(BaseSegment):
     )
 
 
-class ConstraintPropertiesSegment(BaseSegment):
-    """CONSTRAINT clause for CREATE TABLE or ALTER TABLE command.
+class InlineConstraintPropertiesSegment(BaseSegment):
+    """In Line CONSTRAINT clause for CREATE TABLE or ALTER TABLE command.
 
-    https://docs.snowflake.com/en/sql-reference/constraints-properties.html
+    https://docs.snowflake.com/sql-reference/sql/create-table-constraint#syntax-for-inline-constraints
+    """
+
+    type = "constraint_properties_segment"
+    match_grammar = Sequence(
+        Sequence(
+            "CONSTRAINT",
+            Ref("SingleIdentifierGrammar"),
+            optional=True,
+        ),
+        OneOf(
+            Sequence(
+                OneOf(
+                    Ref("PrimaryKeyGrammar"),
+                    Ref("UniqueKeyGrammar"),
+                ),
+                Bracketed(
+                    Delimited(
+                        Ref("ColumnReferenceSegment"),
+                    ),
+                    # For use in CREATE TABLE as a part of
+                    # ColumnDefinitionSegment.ColumnConstraintSegment
+                    optional=True,
+                ),
+            ),
+            Sequence(
+                Sequence(
+                    Ref("ForeignKeyGrammar"),
+                ),
+                "REFERENCES",
+                Ref("TableReferenceSegment"),
+                Bracketed(
+                    Delimited(
+                        Ref("ColumnReferenceSegment"),
+                    ),
+                ),
+                Ref("ForeignKeyConstraintGrammar", optional=True),
+            ),
+        ),
+        Ref("InlineConstraintGrammar", optional=True),
+    )
+
+
+class OutOfLineConstraintPropertiesSegment(BaseSegment):
+    """Our of Line CONSTRAINT clause for CREATE TABLE or ALTER TABLE command.
+
+    https://docs.snowflake.com/sql-reference/sql/create-table-constraint#syntax-for-out-of-line-constraints
     """
 
     type = "constraint_properties_segment"
@@ -3968,7 +4014,7 @@ class ConstraintPropertiesSegment(BaseSegment):
                     Bracketed(
                         Delimited(
                             Ref("ColumnReferenceSegment"),
-                        )
+                        ),
                     ),
                 ),
                 "REFERENCES",
@@ -4032,7 +4078,7 @@ class ColumnConstraintSegment(ansi.ColumnConstraintSegment):
             ),
         ),
         Ref("TagBracketedEqualsSegment", optional=True),
-        Ref("ConstraintPropertiesSegment"),
+        Ref("InlineConstraintPropertiesSegment"),
         Sequence("DEFAULT", Ref("QuotedLiteralSegment")),
         Sequence("CHECK", Bracketed(Ref("ExpressionSegment"))),
         Sequence(  # DEFAULT <value>
@@ -4388,7 +4434,7 @@ class CreateTableStatementSegment(ansi.CreateTableStatementSegment):
     https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table
     """
 
-    match_grammar = Sequence(
+    match_grammar: Matchable = Sequence(
         "CREATE",
         Ref("OrReplaceGrammar", optional=True),
         Ref("TemporaryTransientGrammar", optional=True),
@@ -4427,7 +4473,7 @@ class CreateTableStatementSegment(ansi.CreateTableStatementSegment):
                     Delimited(
                         Sequence(
                             OneOf(
-                                Ref("ConstraintPropertiesSegment"),
+                                Ref("OutOfLineConstraintPropertiesSegment"),
                                 Ref("ColumnDefinitionSegment"),
                                 Ref("SingleIdentifierGrammar"),
                                 Sequence(
@@ -5839,7 +5885,7 @@ class CreateExternalTableSegment(BaseSegment):
                     OptionallyBracketed(
                         Sequence(
                             Ref("ExpressionSegment"),
-                            Ref("ConstraintPropertiesSegment", optional=True),
+                            Ref("InlineConstraintPropertiesSegment", optional=True),
                             Sequence(
                                 Ref.keyword("NOT", optional=True), "NULL", optional=True
                             ),

Test patch: diff --git a/test/fixtures/dialects/snowflake/create_table.sql b/test/fixtures/dialects/snowflake/create_table.sql
index 8691394c437..75779a05f11 100644
--- a/test/fixtures/dialects/snowflake/create_table.sql
+++ b/test/fixtures/dialects/snowflake/create_table.sql
@@ -212,3 +212,30 @@ CREATE TABLE some_schema.some_table
   , some_event_date_time_utc VARCHAR AS (TO_TIMESTAMP(SUBSTR(some_text_value, 5, 13)))
   , some_other_event_date_time_utc TIMESTAMP AS (IFF(is_condition_true AND TRY_TO_NUMBER(some_text_value) IS NOT NULL, TO_TIMESTAMP(SUBSTR(some_text_value, 5, 13)), '1900-01-01')) COMMENT 'The date and time of the other event'
 );
+
+
+CREATE OR REPLACE TABLE some_table (
+  id INTEGER NOT NULL,
+  CONSTRAINT MY_FK FOREIGN KEY (id) REFERENCES another_table(id) MATCH SIMPLE ON DELETE RESTRICT
+);
+
+CREATE OR REPLACE TABLE some_table (
+  id INTEGER NOT NULL,
+  CONSTRAINT MY_FK FOREIGN KEY (id) REFERENCES another_table MATCH FULL ON DELETE RESTRICT
+);
+
+
+CREATE OR REPLACE TABLE some_table (
+    ID INTEGER NOT NULL CONSTRAINT MY_FK
+    FOREIGN KEY REFERENCES another_table (id)
+    MATCH PARTIAL
+    ON DELETE RESTRICT
+    ON UPDATE SET DEFAULT
+);
+
+CREATE OR REPLACE TABLE some_table (
+    ID INTEGER NOT NULL,
+    CONSTRAINT MY_FK FOREIGN KEY (ID) REFERENCES another_table (id)
+    MATCH SIMPLE
+    ON DELETE CASCADE
+);
diff --git a/test/fixtures/dialects/snowflake/create_table.yml b/test/fixtures/dialects/snowflake/create_table.yml
index 94a76bdf4bc..3b125f4a013 100644
--- a/test/fixtures/dialects/snowflake/create_table.yml
+++ b/test/fixtures/dialects/snowflake/create_table.yml
@@ -3,7 +3,7 @@
 # computed by SQLFluff when running the tests. Please run
 # `python test/generate_parse_fixture_yml.py`  to generate them after adding or
 # altering SQL files.
-_hash: 74de85b93e5227b9ab1271d01a558b6ca2e844a1d05659c33297c0d5e51b08a9
+_hash: a337f8d00263c86575e132b7e2950c189c27f0a2af8ac5ff264c25ca6f7aea3c
 file:
 - statement:
     create_table_statement:
@@ -1758,3 +1758,168 @@ file:
           quoted_literal: "'The date and time of the other event'"
       - end_bracket: )
 - statement_terminator: ;
+- statement:
+    create_table_statement:
+    - keyword: CREATE
+    - keyword: OR
+    - keyword: REPLACE
+    - keyword: TABLE
+    - table_reference:
+        naked_identifier: some_table
+    - bracketed:
+        start_bracket: (
+        column_definition:
+          naked_identifier: id
+          data_type:
+            data_type_identifier: INTEGER
+          column_constraint_segment:
+          - keyword: NOT
+          - keyword: 'NULL'
+        comma: ','
+        constraint_properties_segment:
+        - keyword: CONSTRAINT
+        - naked_identifier: MY_FK
+        - keyword: FOREIGN
+        - keyword: KEY
+        - bracketed:
+            start_bracket: (
+            column_reference:
+              naked_identifier: id
+            end_bracket: )
+        - keyword: REFERENCES
+        - table_reference:
+            naked_identifier: another_table
+        - bracketed:
+            start_bracket: (
+            column_reference:
+              naked_identifier: id
+            end_bracket: )
+        - keyword: MATCH
+        - keyword: SIMPLE
+        - keyword: 'ON'
+        - keyword: DELETE
+        - keyword: RESTRICT
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_table_statement:
+    - keyword: CREATE
+    - keyword: OR
+    - keyword: REPLACE
+    - keyword: TABLE
+    - table_reference:
+        naked_identifier: some_table
+    - bracketed:
+        start_bracket: (
+        column_definition:
+          naked_identifier: id
+          data_type:
+            data_type_identifier: INTEGER
+          column_constraint_segment:
+          - keyword: NOT
+          - keyword: 'NULL'
+        comma: ','
+        constraint_properties_segment:
+        - keyword: CONSTRAINT
+        - naked_identifier: MY_FK
+        - keyword: FOREIGN
+        - keyword: KEY
+        - bracketed:
+            start_bracket: (
+            column_reference:
+              naked_identifier: id
+            end_bracket: )
+        - keyword: REFERENCES
+        - table_reference:
+            naked_identifier: another_table
+        - keyword: MATCH
+        - keyword: FULL
+        - keyword: 'ON'
+        - keyword: DELETE
+        - keyword: RESTRICT
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_table_statement:
+    - keyword: CREATE
+    - keyword: OR
+    - keyword: REPLACE
+    - keyword: TABLE
+    - table_reference:
+        naked_identifier: some_table
+    - bracketed:
+        start_bracket: (
+        column_definition:
+          naked_identifier: ID
+          data_type:
+            data_type_identifier: INTEGER
+          column_constraint_segment:
+          - keyword: NOT
+          - keyword: 'NULL'
+          - constraint_properties_segment:
+            - keyword: CONSTRAINT
+            - naked_identifier: MY_FK
+            - keyword: FOREIGN
+            - keyword: KEY
+            - keyword: REFERENCES
+            - table_reference:
+                naked_identifier: another_table
+            - bracketed:
+                start_bracket: (
+                column_reference:
+                  naked_identifier: id
+                end_bracket: )
+            - keyword: MATCH
+            - keyword: PARTIAL
+            - keyword: 'ON'
+            - keyword: DELETE
+            - keyword: RESTRICT
+            - keyword: 'ON'
+            - keyword: UPDATE
+            - keyword: SET
+            - keyword: DEFAULT
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_table_statement:
+    - keyword: CREATE
+    - keyword: OR
+    - keyword: REPLACE
+    - keyword: TABLE
+    - table_reference:
+        naked_identifier: some_table
+    - bracketed:
+        start_bracket: (
+        column_definition:
+          naked_identifier: ID
+          data_type:
+            data_type_identifier: INTEGER
+          column_constraint_segment:
+          - keyword: NOT
+          - keyword: 'NULL'
+        comma: ','
+        constraint_properties_segment:
+        - keyword: CONSTRAINT
+        - naked_identifier: MY_FK
+        - keyword: FOREIGN
+        - keyword: KEY
+        - bracketed:
+            start_bracket: (
+            column_reference:
+              naked_identifier: ID
+            end_bracket: )
+        - keyword: REFERENCES
+        - table_reference:
+            naked_identifier: another_table
+        - bracketed:
+            start_bracket: (
+            column_reference:
+              naked_identifier: id
+            end_bracket: )
+        - keyword: MATCH
+        - keyword: SIMPLE
+        - keyword: 'ON'
+        - keyword: DELETE
+        - keyword: CASCADE
+        end_bracket: )
+- statement_terminator: ;