Repository: sqlfluff/sqlfluff
Pull request: Added trigger support in ANSI - and extended it in Postgres
### Brief summary of the change made
Added CreateTrigger and DropTrigger statement segments in ANSI. ANSI doesn't support Alter Trigger.
Added AlterTrigger in Postgres - and extended Create and Drop to match the PG14 docs. ANSI definitions were reverse engineered from the Postgres docs (what they said they didn't support).

Fixes #1815

Partially addresses #1487

### Are there any other side effects of this change that we should be aware of?
No

### Pull Request checklist
Docstrings with references to the docs added.
Test cases added and passing.

Problem statement:
Postgresql: Found unparsable section: DROP TRIGGER IF EXISTS my_trigger ON something;
# Expected Behaviour

The code snippet is parsed successfully.

# Observed Behaviour

PRS error

`L:  41 | P:   1 |  PRS | Line 41, Position 1: Found unparsable section: DROP TRIGGER IF EXISTS my_trigger ON something;'`

# Steps to Reproduce

Parse this with postgres dialect:

`DROP TRIGGER IF EXISTS my_trigger ON something;`

# Configuration

- Command: `sqlfluff lint --dialect=postgres --exclude-rules L039,L016 --processes 2 --ignore PRS path/to/sql/dir-files`
- SQLfluff 0.7.1
- Python 3.9.x

Hints from issue discussion:

Commit message: Added trigger support in ANSI - and extended it in postgres

Patch to review:
diff --git a/src/sqlfluff/dialects/dialect_ansi.py b/src/sqlfluff/dialects/dialect_ansi.py
index 0694f858850..351b0d1d6cc 100644
--- a/src/sqlfluff/dialects/dialect_ansi.py
+++ b/src/sqlfluff/dialects/dialect_ansi.py
@@ -742,6 +742,13 @@ class SequenceReferenceSegment(ObjectReferenceSegment):
     type = "sequence_reference"
 
 
+@ansi_dialect.segment()
+class TriggerReferenceSegment(ObjectReferenceSegment):
+    """A reference to a trigger."""
+
+    type = "trigger_reference"
+
+
 @ansi_dialect.segment()
 class SingleIdentifierListSegment(BaseSegment):
     """A comma delimited list of identifiers."""
@@ -2952,6 +2959,8 @@ class StatementSegment(BaseSegment):
         Ref("CreateSequenceStatementSegment"),
         Ref("AlterSequenceStatementSegment"),
         Ref("DropSequenceStatementSegment"),
+        Ref("CreateTriggerStatementSegment"),
+        Ref("DropTriggerStatementSegment"),
     )
 
     def get_table_references(self):
@@ -3157,3 +3166,86 @@ class DatePartFunctionNameSegment(BaseSegment):
 
     type = "function_name"
     match_grammar = Sequence("DATEADD")
+
+
+@ansi_dialect.segment()
+class CreateTriggerStatementSegment(BaseSegment):
+    """Create Trigger Statement.
+
+    Taken from specification in https://www.postgresql.org/docs/14/sql-createtrigger.html
+    Edited as per notes in above - what doesn't match ANSI
+    """
+
+    type = "create_trigger"
+
+    match_grammar = Sequence("CREATE", "TRIGGER", Anything())
+
+    parse_grammar = Sequence(
+        "CREATE",
+        "TRIGGER",
+        Ref("TriggerReferenceSegment"),
+        OneOf("BEFORE", "AFTER", Sequence("INSTEAD", "OF")),
+        Delimited(
+            "INSERT",
+            "DELETE",
+            Sequence(
+                "UPDATE",
+                "OF",
+                Delimited(
+                    Ref("ColumnReferenceSegment"),
+                    terminator=OneOf("INSERT", "DELETE", "UPDATE", "ON"),
+                ),
+            ),
+            delimiter="OR",
+            terminator="ON",
+        ),
+        "ON",
+        Ref("TableReferenceSegment"),
+        AnyNumberOf(
+            Sequence(
+                "REFERENCING",
+                "OLD",
+                "ROW",
+                "AS",
+                Ref("ParameterNameSegment"),
+                "NEW",
+                "ROW",
+                "AS",
+                Ref("ParameterNameSegment"),
+            ),
+            Sequence("FROM", Ref("TableReferenceSegment")),
+            OneOf(
+                Sequence("NOT", "DEFERRABLE"),
+                Sequence(
+                    Ref.keyword("DEFERRABLE", optional=True),
+                    OneOf(
+                        Sequence("INITIALLY", "IMMEDIATE"),
+                        Sequence("INITIALLY", "DEFERRED"),
+                    ),
+                ),
+            ),
+            Sequence(
+                "FOR", Ref.keyword("EACH", optional=True), OneOf("ROW", "STATEMENT")
+            ),
+            Sequence("WHEN", Bracketed(Ref("ExpressionSegment"))),
+        ),
+        Sequence(
+            "EXECUTE",
+            "PROCEDURE",
+            Ref("FunctionNameIdentifierSegment"),
+            Bracketed(Ref("FunctionContentsGrammar")),
+        ),
+    )
+
+
+@ansi_dialect.segment()
+class DropTriggerStatementSegment(BaseSegment):
+    """Drop Trigger Statement.
+
+    Taken from specification in https://www.postgresql.org/docs/14/sql-droptrigger.html
+    Edited as per notes in above - what doesn't match ANSI
+    """
+
+    type = "drop_trigger"
+
+    match_grammar = Sequence("DROP", "TRIGGER", Ref("TriggerReferenceSegment"))
diff --git a/src/sqlfluff/dialects/dialect_postgres.py b/src/sqlfluff/dialects/dialect_postgres.py
index 6fba34627ed..65d9056ccb1 100644
--- a/src/sqlfluff/dialects/dialect_postgres.py
+++ b/src/sqlfluff/dialects/dialect_postgres.py
@@ -2119,6 +2119,7 @@ class StatementSegment(BaseSegment):
             Ref("CommentOnStatementSegment"),
             Ref("AnalyzeStatementSegment"),
             Ref("CreateTableAsStatementSegment"),
+            Ref("AlterTriggerStatementSegment"),
         ],
     )
 
@@ -2150,3 +2151,136 @@ class FunctionSegment(BaseSegment):
         ),
         Ref("PostFunctionGrammar", optional=True),
     )
+
+
+@postgres_dialect.segment(replace=True)
+class CreateTriggerStatementSegment(BaseSegment):
+    """Create Trigger Statement.
+
+    As Specified in https://www.postgresql.org/docs/14/sql-createtrigger.html
+    """
+
+    type = "create_trigger"
+
+    match_grammar = Sequence(
+        "CREATE",
+        Sequence("OR", "REPLACE", optional=True),
+        Ref.keyword("CONSTRAINT", optional=True),
+        "TRIGGER",
+        Anything(),
+    )
+
+    parse_grammar = Sequence(
+        "CREATE",
+        Sequence("OR", "REPLACE", optional=True),
+        Ref.keyword("CONSTRAINT", optional=True),
+        "TRIGGER",
+        Ref("TriggerReferenceSegment"),
+        OneOf("BEFORE", "AFTER", Sequence("INSTEAD", "OF")),
+        Delimited(
+            "INSERT",
+            "DELETE",
+            "TRUNCATE",
+            Sequence(
+                "UPDATE",
+                "OF",
+                Delimited(
+                    Ref("ColumnReferenceSegment"),
+                    terminator=OneOf("INSERT", "DELETE", "UPDATE", "TRUNCATE", "ON"),
+                ),
+            ),
+            delimiter="OR",
+            terminator="ON",
+        ),
+        "ON",
+        Ref("TableReferenceSegment"),
+        AnyNumberOf(
+            Sequence("FROM", Ref("TableReferenceSegment")),
+            OneOf(
+                Sequence("NOT", "DEFERRABLE"),
+                Sequence(
+                    Ref.keyword("DEFERRABLE", optional=True),
+                    OneOf(
+                        Sequence("INITIALLY", "IMMEDIATE"),
+                        Sequence("INITIALLY", "DEFERRED"),
+                    ),
+                ),
+            ),
+            Sequence(
+                "REFERENCING",
+                OneOf("OLD", "NEW"),
+                "TABLE",
+                "AS",
+                Ref("TableReferenceSegment"),
+                Sequence(
+                    OneOf("OLD", "NEW"),
+                    "TABLE",
+                    "AS",
+                    Ref("TableReferenceSegment"),
+                    optional=True,
+                ),
+            ),
+            Sequence(
+                "FOR", Ref.keyword("EACH", optional=True), OneOf("ROW", "STATEMENT")
+            ),
+            Sequence("WHEN", Bracketed(Ref("ExpressionSegment"))),
+        ),
+        Sequence(
+            "EXECUTE",
+            OneOf("FUNCTION", "PROCEDURE"),
+            Ref("FunctionNameIdentifierSegment"),
+            Bracketed(Ref("FunctionContentsGrammar")),
+        ),
+    )
+
+
+@postgres_dialect.segment()
+class AlterTriggerStatementSegment(BaseSegment):
+    """Alter Trigger Statement.
+
+    As Specified in https://www.postgresql.org/docs/14/sql-altertrigger.html
+    """
+
+    type = "alter_trigger"
+
+    match_grammar = Sequence("ALTER", "TRIGGER", Anything())
+
+    parse_grammar = Sequence(
+        "ALTER",
+        "TRIGGER",
+        Ref("TriggerReferenceSegment"),
+        "ON",
+        Ref("TableReferenceSegment"),
+        OneOf(
+            Sequence("RENAME", "TO", Ref("TriggerReferenceSegment")),
+            Sequence(
+                Ref.keyword("NO", optional=True),
+                "DEPENDS",
+                "ON",
+                "EXTENSION",
+                Ref("ParameterNameSegment"),
+            ),
+        ),
+    )
+
+
+@postgres_dialect.segment(replace=True)
+class DropTriggerStatementSegment(BaseSegment):
+    """Drop Trigger Statement.
+
+    As Specified in https://www.postgresql.org/docs/14/sql-droptrigger.html
+    """
+
+    type = "drop_trigger"
+
+    match_grammar = Sequence("DROP", "TRIGGER", Anything())
+
+    parse_grammar = Sequence(
+        "DROP",
+        "TRIGGER",
+        Sequence("IF", "EXISTS", optional=True),
+        Ref("TriggerReferenceSegment"),
+        "ON",
+        Ref("TableReferenceSegment"),
+        OneOf("CASCADE", "RESTRICT", optional=True),
+    )
diff --git a/test/fixtures/dialects/ansi/create_trigger.sql b/test/fixtures/dialects/ansi/create_trigger.sql
new file mode 100644
index 00000000000..8fdae69e5fb
--- /dev/null
+++ b/test/fixtures/dialects/ansi/create_trigger.sql
@@ -0,0 +1,10 @@
+CREATE TRIGGER foo BEFORE INSERT ON bar EXECUTE PROCEDURE proc(args);
+
+CREATE TRIGGER foo BEFORE INSERT on bar EXECUTE PROCEDURE proc(args);
+
+CREATE TRIGGER foo AFTER UPDATE OF bar, baz ON bar EXECUTE PROCEDURE proc(args);
+
+CREATE TRIGGER foo INSTEAD OF DELETE ON bar FROM baz DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE PROCEDURE proc(args);
+
+CREATE TRIGGER foo BEFORE INSERT ON bar WHEN (a=b) EXECUTE PROCEDURE proc(args);
+
diff --git a/test/fixtures/dialects/ansi/create_trigger.yml b/test/fixtures/dialects/ansi/create_trigger.yml
new file mode 100644
index 00000000000..86c087b7fad
--- /dev/null
+++ b/test/fixtures/dialects/ansi/create_trigger.yml
@@ -0,0 +1,138 @@
+# YML test files are auto-generated from SQL files and should not be edited by
+# hand. To help enforce this, the "hash" field in the file must match a hash
+# 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: 9a9d8a07762da78a9691d6e3b4c84875377a2c0c8f744b69598ab161391e95b5
+file:
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: BEFORE
+    - keyword: INSERT
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: EXECUTE
+    - keyword: PROCEDURE
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: BEFORE
+    - keyword: INSERT
+    - keyword: 'on'
+    - table_reference:
+        identifier: bar
+    - keyword: EXECUTE
+    - keyword: PROCEDURE
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: AFTER
+    - keyword: UPDATE
+    - keyword: OF
+    - column_reference:
+        identifier: bar
+    - comma: ','
+    - column_reference:
+        identifier: baz
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: EXECUTE
+    - keyword: PROCEDURE
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: INSTEAD
+    - keyword: OF
+    - keyword: DELETE
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: FROM
+    - table_reference:
+        identifier: baz
+    - keyword: DEFERRABLE
+    - keyword: INITIALLY
+    - keyword: IMMEDIATE
+    - keyword: FOR
+    - keyword: EACH
+    - keyword: ROW
+    - keyword: EXECUTE
+    - keyword: PROCEDURE
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: BEFORE
+    - keyword: INSERT
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: WHEN
+    - bracketed:
+        start_bracket: (
+        expression:
+        - column_reference:
+            identifier: a
+        - comparison_operator: '='
+        - column_reference:
+            identifier: b
+        end_bracket: )
+    - keyword: EXECUTE
+    - keyword: PROCEDURE
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
diff --git a/test/fixtures/dialects/ansi/drop_trigger.sql b/test/fixtures/dialects/ansi/drop_trigger.sql
new file mode 100644
index 00000000000..8d0319b7759
--- /dev/null
+++ b/test/fixtures/dialects/ansi/drop_trigger.sql
@@ -0,0 +1 @@
+DROP TRIGGER foo;
diff --git a/test/fixtures/dialects/ansi/drop_trigger.yml b/test/fixtures/dialects/ansi/drop_trigger.yml
new file mode 100644
index 00000000000..724f040de00
--- /dev/null
+++ b/test/fixtures/dialects/ansi/drop_trigger.yml
@@ -0,0 +1,14 @@
+# YML test files are auto-generated from SQL files and should not be edited by
+# hand. To help enforce this, the "hash" field in the file must match a hash
+# 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: 2ea1231eaacd7fd39b30ab2e7cf3d45565bfa4d3ab30bc1e097d0560b558212f
+file:
+  statement:
+    drop_trigger:
+    - keyword: DROP
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+  statement_terminator: ;
diff --git a/test/fixtures/dialects/postgres/postgres_alter_trigger.sql b/test/fixtures/dialects/postgres/postgres_alter_trigger.sql
new file mode 100644
index 00000000000..f30fce33eeb
--- /dev/null
+++ b/test/fixtures/dialects/postgres/postgres_alter_trigger.sql
@@ -0,0 +1,5 @@
+ALTER TRIGGER foo ON table_name RENAME TO new_name;
+
+ALTER TRIGGER foo ON table_name DEPENDS ON EXTENSION extension_name;
+
+ALTER TRIGGER foo ON table_name NO DEPENDS ON EXTENSION extension_name;
diff --git a/test/fixtures/dialects/postgres/postgres_alter_trigger.yml b/test/fixtures/dialects/postgres/postgres_alter_trigger.yml
new file mode 100644
index 00000000000..9355ba15500
--- /dev/null
+++ b/test/fixtures/dialects/postgres/postgres_alter_trigger.yml
@@ -0,0 +1,50 @@
+# YML test files are auto-generated from SQL files and should not be edited by
+# hand. To help enforce this, the "hash" field in the file must match a hash
+# 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: 4ea5b773e8d1b4ea49db24c1e4b51ffbbf2aa7792a1622cccd394f7336ad5ce7
+file:
+- statement:
+    alter_trigger:
+    - keyword: ALTER
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: 'ON'
+    - table_reference:
+        identifier: table_name
+    - keyword: RENAME
+    - keyword: TO
+    - trigger_reference:
+        identifier: new_name
+- statement_terminator: ;
+- statement:
+    alter_trigger:
+    - keyword: ALTER
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: 'ON'
+    - table_reference:
+        identifier: table_name
+    - keyword: DEPENDS
+    - keyword: 'ON'
+    - keyword: EXTENSION
+    - parameter: extension_name
+- statement_terminator: ;
+- statement:
+    alter_trigger:
+    - keyword: ALTER
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: 'ON'
+    - table_reference:
+        identifier: table_name
+    - keyword: 'NO'
+    - keyword: DEPENDS
+    - keyword: 'ON'
+    - keyword: EXTENSION
+    - parameter: extension_name
+- statement_terminator: ;
diff --git a/test/fixtures/dialects/postgres/postgres_create_trigger.sql b/test/fixtures/dialects/postgres/postgres_create_trigger.sql
new file mode 100644
index 00000000000..b942b722549
--- /dev/null
+++ b/test/fixtures/dialects/postgres/postgres_create_trigger.sql
@@ -0,0 +1,13 @@
+CREATE TRIGGER foo AFTER INSERT ON bar EXECUTE FUNCTION proc(args);
+
+CREATE TRIGGER foo BEFORE INSERT on bar EXECUTE FUNCTION proc(args);
+
+CREATE TRIGGER foo AFTER UPDATE OF bar, baz ON bar EXECUTE FUNCTION proc(args);
+
+CREATE TRIGGER foo INSTEAD OF DELETE ON bar FROM baz DEFERRABLE INITIALLY DEFERRED FOR EACH STATEMENT EXECUTE FUNCTION proc(args);
+
+CREATE TRIGGER foo BEFORE INSERT ON bar WHEN (a=b) EXECUTE FUNCTION proc(args);
+
+CREATE OR REPLACE CONSTRAINT TRIGGER foo BEFORE INSERT ON bar EXECUTE FUNCTION proc(args);
+
+CREATE TRIGGER foo BEFORE INSERT ON bar REFERENCING OLD TABLE as old_table NEW TABLE AS new_table EXECUTE PROCEDURE proc(args);
diff --git a/test/fixtures/dialects/postgres/postgres_create_trigger.yml b/test/fixtures/dialects/postgres/postgres_create_trigger.yml
new file mode 100644
index 00000000000..156f17a3cb7
--- /dev/null
+++ b/test/fixtures/dialects/postgres/postgres_create_trigger.yml
@@ -0,0 +1,194 @@
+# YML test files are auto-generated from SQL files and should not be edited by
+# hand. To help enforce this, the "hash" field in the file must match a hash
+# 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: 9bf176d1c53eeebb7a130386fee636d424265f6ab888f97e8b3b1da86cde9a37
+file:
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: AFTER
+    - keyword: INSERT
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: EXECUTE
+    - keyword: FUNCTION
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: BEFORE
+    - keyword: INSERT
+    - keyword: 'on'
+    - table_reference:
+        identifier: bar
+    - keyword: EXECUTE
+    - keyword: FUNCTION
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: AFTER
+    - keyword: UPDATE
+    - keyword: OF
+    - column_reference:
+        identifier: bar
+    - comma: ','
+    - column_reference:
+        identifier: baz
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: EXECUTE
+    - keyword: FUNCTION
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: INSTEAD
+    - keyword: OF
+    - keyword: DELETE
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: FROM
+    - table_reference:
+        identifier: baz
+    - keyword: DEFERRABLE
+    - keyword: INITIALLY
+    - keyword: DEFERRED
+    - keyword: FOR
+    - keyword: EACH
+    - keyword: STATEMENT
+    - keyword: EXECUTE
+    - keyword: FUNCTION
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: BEFORE
+    - keyword: INSERT
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: WHEN
+    - bracketed:
+        start_bracket: (
+        expression:
+        - column_reference:
+            identifier: a
+        - comparison_operator: '='
+        - column_reference:
+            identifier: b
+        end_bracket: )
+    - keyword: EXECUTE
+    - keyword: FUNCTION
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - binary_operator: OR
+    - keyword: REPLACE
+    - keyword: CONSTRAINT
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: BEFORE
+    - keyword: INSERT
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: EXECUTE
+    - keyword: FUNCTION
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    create_trigger:
+    - keyword: CREATE
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: BEFORE
+    - keyword: INSERT
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: REFERENCING
+    - keyword: OLD
+    - keyword: TABLE
+    - keyword: as
+    - table_reference:
+        identifier: old_table
+    - keyword: NEW
+    - keyword: TABLE
+    - keyword: AS
+    - table_reference:
+        identifier: new_table
+    - keyword: EXECUTE
+    - keyword: PROCEDURE
+    - function_name_identifier: proc
+    - bracketed:
+        start_bracket: (
+        expression:
+          column_reference:
+            identifier: args
+        end_bracket: )
+- statement_terminator: ;
diff --git a/test/fixtures/dialects/postgres/postgres_drop_trigger.sql b/test/fixtures/dialects/postgres/postgres_drop_trigger.sql
new file mode 100644
index 00000000000..73675b8c058
--- /dev/null
+++ b/test/fixtures/dialects/postgres/postgres_drop_trigger.sql
@@ -0,0 +1,7 @@
+DROP TRIGGER foo ON bar;
+
+DROP TRIGGER IF EXISTS foo ON bar;
+
+DROP TRIGGER foo ON bar CASCADE;
+
+DROP TRIGGER IF EXISTS foo ON bar RESTRICT;
diff --git a/test/fixtures/dialects/postgres/postgres_drop_trigger.yml b/test/fixtures/dialects/postgres/postgres_drop_trigger.yml
new file mode 100644
index 00000000000..072008b3cda
--- /dev/null
+++ b/test/fixtures/dialects/postgres/postgres_drop_trigger.yml
@@ -0,0 +1,53 @@
+# YML test files are auto-generated from SQL files and should not be edited by
+# hand. To help enforce this, the "hash" field in the file must match a hash
+# 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: 0d43a8fc970d5aca71c621c02562da1afb9ecbea1f01c146ef2a3946c4e474b4
+file:
+- statement:
+    drop_trigger:
+    - keyword: DROP
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+- statement_terminator: ;
+- statement:
+    drop_trigger:
+    - keyword: DROP
+    - keyword: TRIGGER
+    - keyword: IF
+    - keyword: EXISTS
+    - trigger_reference:
+        identifier: foo
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+- statement_terminator: ;
+- statement:
+    drop_trigger:
+    - keyword: DROP
+    - keyword: TRIGGER
+    - trigger_reference:
+        identifier: foo
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: CASCADE
+- statement_terminator: ;
+- statement:
+    drop_trigger:
+    - keyword: DROP
+    - keyword: TRIGGER
+    - keyword: IF
+    - keyword: EXISTS
+    - trigger_reference:
+        identifier: foo
+    - keyword: 'ON'
+    - table_reference:
+        identifier: bar
+    - keyword: RESTRICT
+- statement_terminator: ;


Code review comments:
diff_hunk: @@ -3157,3 +3166,86 @@ class DatePartFunctionNameSegment(BaseSegment):
 
     type = "function_name"
     match_grammar = Sequence("DATEADD")
+
+
+@ansi_dialect.segment()
+class CreateTriggerStatementSegment(BaseSegment):
+    """Create Trigger Statement.
+
+    Taken from specification in https://www.postgresql.org/docs/14/sql-createtrigger.html
+    Edited as per notes in above - what doesn't match ANSI
+    """
+
+    type = "create_trigger"
+
+    match_grammar = Sequence("CREATE", "TRIGGER", Anything())
+
+    parse_grammar = Sequence(
+        "CREATE",
+        "TRIGGER",
+        Ref("TriggerReferenceSegment"),
+        OneOf("BEFORE", "AFTER", Sequence("INSTEAD", "OF")),
+        Delimited(
+            "INSERT",
+            "DELETE",
+            Sequence(
+                "UPDATE",
+                "OF",
+                Delimited(
+                    Ref("ColumnReferenceSegment"),
+                    terminator=OneOf("INSERT", "DELETE", "UPDATE", "ON"),
path: src/sqlfluff/dialects/dialect_ansi.py
text: @user1:
Do "INSERT", "DELETE", "UPDATE" need to be in here?

@author:
Ah yes - that should have been OR or ON