Repository: sqlfluff/sqlfluff
Pull request: Add INSERT INTO statements to Redshift Dialect
### Brief summary of the change made
Fixes #1895 , the Insert Into statement was added to the Redshift dialect. Note that this differs from the ANSI/Postgres dialects in a few ways. Postgres, for example, can upsert but that is not a possibility in Redshift.

### Are there any other side effects of this change that we should be aware of?
None at this time

### Pull Request checklist
- [x] Please confirm you have completed any of the necessary steps below.

- Included test cases to demonstrate any code changes, which may be one or more of the following:
  - `.yml` rule test cases in `test/fixtures/rules/std_rule_cases`.
  - `.sql`/`.yml` parser test cases in `test/fixtures/dialects` (note YML files can be auto generated with `python test/generate_parse_fixture_yml.py` or by running `tox` locally).
  - Full autofix test cases in `test/fixtures/linter/autofix`.
  - Other.
- Added appropriate documentation for the change.
- Created GitHub issues for any relevant followup/future enhancements if appropriate.


Problem statement:
Redshift Dialect Cannot Parse INSERT INTO Statement
## Expected Behaviour
SQLFluff ought to be able to parse the following SQL statement using the Redshift dialect:
```
INSERT INTO s1.t1 (
    SELECT
        col1,
        col2,
        col3
    FROM testtable
  );
```

## Observed Behaviour
SQLFluff notes that this section is unparsable.

## Steps to Reproduce
Run the aforementioned query.

## Dialect
Redshift

## Version
`0.8.1`

## Configuration
Default

Hints from issue discussion:

Commit message: Formatting and removed Insert Statement from StatementSegment

Patch to review:
diff --git a/src/sqlfluff/dialects/dialect_redshift.py b/src/sqlfluff/dialects/dialect_redshift.py
index fd80d013d8c..f8c83e2bc56 100644
--- a/src/sqlfluff/dialects/dialect_redshift.py
+++ b/src/sqlfluff/dialects/dialect_redshift.py
@@ -13,6 +13,7 @@
     BaseSegment,
     Delimited,
     Nothing,
+    OptionallyBracketed,
 )
 
 from sqlfluff.core.dialects import load_raw_dialect
@@ -56,17 +57,6 @@ class DatePartFunctionNameSegment(BaseSegment):
     match_grammar = OneOf("DATEADD", "DATEDIFF")
 
 
-@redshift_dialect.segment(replace=True)
-class FunctionSegment(BaseSegment):
-    """A scalar or aggregate function.
-
-    Revert back to the ANSI definition to support ignore nulls
-    """
-
-    type = "function"
-    match_grammar = ansi_dialect.get_segment("FunctionSegment").match_grammar.copy()
-
-
 @redshift_dialect.segment()
 class ColumnEncodingSegment(BaseSegment):
     """ColumnEncoding segment.
@@ -145,6 +135,17 @@ class ColumnConstraintSegment(BaseSegment):
     )
 
 
+@redshift_dialect.segment(replace=True)
+class FunctionSegment(BaseSegment):
+    """A scalar or aggregate function.
+
+    Revert back to the ANSI definition to support ignore nulls
+    """
+
+    type = "function"
+    match_grammar = ansi_dialect.get_segment("FunctionSegment").match_grammar.copy()
+
+
 @redshift_dialect.segment()
 class TableAttributeSegment(BaseSegment):
     """Redshift specific table attributes.
@@ -249,6 +250,34 @@ class CreateTableStatementSegment(BaseSegment):
     )
 
 
+@redshift_dialect.segment(replace=True)
+class InsertStatementSegment(BaseSegment):
+    """A `INSERT` statement.
+
+    Redshift has two versions of insert statements:
+        - https://docs.aws.amazon.com/redshift/latest/dg/r_INSERT_30.html
+        - https://docs.aws.amazon.com/redshift/latest/dg/r_INSERT_external_table.html
+    """
+
+    type = "insert_statement"
+    match_grammar = Sequence(
+        "INSERT",
+        "INTO",
+        Ref("TableReferenceSegment"),
+        OneOf(
+            OptionallyBracketed(Ref("SelectableGrammar")),
+            Sequence("DEFAULT", "VALUES"),
+            Sequence(
+                Ref("BracketedColumnReferenceListGrammar", optional=True),
+                OneOf(
+                    Ref("ValuesClauseSegment"),
+                    OptionallyBracketed(Ref("SelectableGrammar")),
+                ),
+            ),
+        ),
+    )
+
+
 # Adding Redshift specific statements
 @redshift_dialect.segment(replace=True)
 class StatementSegment(BaseSegment):
diff --git a/test/fixtures/dialects/redshift/redshift_insert_into.sql b/test/fixtures/dialects/redshift/redshift_insert_into.sql
new file mode 100644
index 00000000000..00da3c78179
--- /dev/null
+++ b/test/fixtures/dialects/redshift/redshift_insert_into.sql
@@ -0,0 +1,30 @@
+INSERT INTO s1.t1 (
+    SELECT
+        col1,
+        col2,
+        col3
+    FROM testtable
+  );
+
+INSERT INTO s1.t1 (col1, col2) (
+    select
+        *
+    from testtable
+);
+
+INSERT INTO schema1.t1
+    SELECT
+        *
+    FROM testtable
+;
+
+INSERT INTO schema1.t1
+    DEFAULT VALUES
+;
+
+INSERT INTO s1.t1 (col1, col2) VALUES
+    ('V1', 1),
+    ('V2', 2)
+;
+
+
diff --git a/test/fixtures/dialects/redshift/redshift_insert_into.yml b/test/fixtures/dialects/redshift/redshift_insert_into.yml
new file mode 100644
index 00000000000..9d33131fb88
--- /dev/null
+++ b/test/fixtures/dialects/redshift/redshift_insert_into.yml
@@ -0,0 +1,140 @@
+# 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: 8f1326efe17254b5a0f1c7b599972309a2f479e22b4c842e6018cfe072672025
+file:
+- statement:
+    insert_statement:
+    - keyword: INSERT
+    - keyword: INTO
+    - table_reference:
+      - identifier: s1
+      - dot: .
+      - identifier: t1
+    - bracketed:
+        start_bracket: (
+        select_statement:
+          select_clause:
+          - keyword: SELECT
+          - select_clause_element:
+              column_reference:
+                identifier: col1
+          - comma: ','
+          - select_clause_element:
+              column_reference:
+                identifier: col2
+          - comma: ','
+          - select_clause_element:
+              column_reference:
+                identifier: col3
+          from_clause:
+            keyword: FROM
+            from_expression:
+              from_expression_element:
+                table_expression:
+                  table_reference:
+                    identifier: testtable
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    insert_statement:
+    - keyword: INSERT
+    - keyword: INTO
+    - table_reference:
+      - identifier: s1
+      - dot: .
+      - identifier: t1
+    - bracketed:
+      - start_bracket: (
+      - column_reference:
+          identifier: col1
+      - comma: ','
+      - column_reference:
+          identifier: col2
+      - end_bracket: )
+    - bracketed:
+        start_bracket: (
+        select_statement:
+          select_clause:
+            keyword: select
+            select_clause_element:
+              wildcard_expression:
+                wildcard_identifier:
+                  star: '*'
+          from_clause:
+            keyword: from
+            from_expression:
+              from_expression_element:
+                table_expression:
+                  table_reference:
+                    identifier: testtable
+        end_bracket: )
+- statement_terminator: ;
+- statement:
+    insert_statement:
+    - keyword: INSERT
+    - keyword: INTO
+    - table_reference:
+      - identifier: schema1
+      - dot: .
+      - identifier: t1
+    - select_statement:
+        select_clause:
+          keyword: SELECT
+          select_clause_element:
+            wildcard_expression:
+              wildcard_identifier:
+                star: '*'
+        from_clause:
+          keyword: FROM
+          from_expression:
+            from_expression_element:
+              table_expression:
+                table_reference:
+                  identifier: testtable
+- statement_terminator: ;
+- statement:
+    insert_statement:
+    - keyword: INSERT
+    - keyword: INTO
+    - table_reference:
+      - identifier: schema1
+      - dot: .
+      - identifier: t1
+    - keyword: DEFAULT
+    - keyword: VALUES
+- statement_terminator: ;
+- statement:
+    insert_statement:
+    - keyword: INSERT
+    - keyword: INTO
+    - table_reference:
+      - identifier: s1
+      - dot: .
+      - identifier: t1
+    - bracketed:
+      - start_bracket: (
+      - column_reference:
+          identifier: col1
+      - comma: ','
+      - column_reference:
+          identifier: col2
+      - end_bracket: )
+    - values_clause:
+      - keyword: VALUES
+      - bracketed:
+        - start_bracket: (
+        - literal: "'V1'"
+        - comma: ','
+        - literal: '1'
+        - end_bracket: )
+      - comma: ','
+      - bracketed:
+        - start_bracket: (
+        - literal: "'V2'"
+        - comma: ','
+        - literal: '2'
+        - end_bracket: )
+- statement_terminator: ;


Code review comments:
diff_hunk: @@ -249,6 +250,34 @@ class CreateTableStatementSegment(BaseSegment):
     )
 
 
+@redshift_dialect.segment(replace=True)
+class InsertStatementSegment(BaseSegment):
+    """A `INSERT` statement.
path: src/sqlfluff/dialects/dialect_redshift.py
text: @user1:
```suggestion
    """An `INSERT` statement.
```