Repository: sqlfluff/sqlfluff
Issue category: Bug Report

Impala `CREATE TABLE AS SELECT` unparsable
### Search before asking

- [X] I searched the [issues](https://github.com/sqlfluff/sqlfluff/issues) and found no similar issues.


### What Happened

SQLfluff fails to parse `CREATE TABLE ... AS SELECT ...` queries, despite being supported by Impala - see [docs](https://impala.apache.org/docs/build/html/topics/impala_create_table.html).

### Expected Behaviour

This should be parsed without any failures.

### Observed Behaviour

```
== [stdin] FAIL
L:   1 | P:   1 |  PRS | Line 1, Position 1: Found unparsable section: 'CREATE
                       | TABLE foo AS SELECT col FROM bar'
WARNING: Parsing errors found and dialect is set to 'impala'. Have you configured your dialect correctly?
```

### How to reproduce

```sh
echo 'CREATE TABLE foo AS SELECT col FROM bar' | sqlfluff lint --dialect=impala -
```

### Dialect

Impala

### Version

sqlfluff: not yet a version - main @ 0e333c3
Python: 3.10.13

### Configuration

```
[sqlfluff]
dialect = impala
```

### 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_impala.py:StatementSegment
src/sqlfluff/dialects/dialect_impala.py:CreateTableStatementSegment
Functions added: src/sqlfluff/dialects/dialect_impala.py:CreateTableAsSelectStatementSegment

Patch:
diff --git a/src/sqlfluff/dialects/dialect_impala.py b/src/sqlfluff/dialects/dialect_impala.py
index 63226f05078..dfe21d30cfc 100644
--- a/src/sqlfluff/dialects/dialect_impala.py
+++ b/src/sqlfluff/dialects/dialect_impala.py
@@ -42,6 +42,7 @@ class StatementSegment(hive.StatementSegment):
 
     match_grammar = hive.StatementSegment.match_grammar.copy(
         insert=[
+            Ref("CreateTableAsSelectStatementSegment"),
             Ref("ComputeStatsStatementSegment"),
             Ref("InsertStatementSegment"),
         ]
@@ -86,67 +87,134 @@ class CreateTableStatementSegment(hive.CreateTableStatementSegment):
         "TABLE",
         Ref("IfNotExistsGrammar", optional=True),
         Ref("TableReferenceSegment"),
+        Bracketed(
+            Delimited(
+                OneOf(
+                    Ref("TableConstraintSegment", optional=True),
+                    Sequence(
+                        Ref("ColumnDefinitionSegment"),
+                        Ref("CommentGrammar", optional=True),
+                    ),
+                ),
+                bracket_pairs_set="angle_bracket_pairs",
+            ),
+            optional=True,
+        ),
         Sequence(
+            "PARTITIONED",
+            "BY",
             Bracketed(
                 Delimited(
-                    OneOf(
-                        Ref("TableConstraintSegment", optional=True),
-                        Sequence(
+                    Sequence(
+                        OneOf(
                             Ref("ColumnDefinitionSegment"),
-                            Ref("CommentGrammar", optional=True),
+                            Ref("SingleIdentifierGrammar"),
                         ),
+                        Ref("CommentGrammar", optional=True),
                     ),
-                    bracket_pairs_set="angle_bracket_pairs",
                 ),
-                optional=True,
             ),
-            Sequence(
-                "PARTITIONED",
-                "BY",
-                Bracketed(
-                    Delimited(
-                        Sequence(
-                            OneOf(
-                                Ref("ColumnDefinitionSegment"),
-                                Ref("SingleIdentifierGrammar"),
-                            ),
-                            Ref("CommentGrammar", optional=True),
-                        ),
+            optional=True,
+        ),
+        Sequence(
+            "SORT",
+            "BY",
+            Bracketed(Delimited(Sequence(Ref("ColumnReferenceSegment")))),
+            optional=True,
+        ),
+        Ref("CommentGrammar", optional=True),
+        Ref("RowFormatClauseSegment", optional=True),
+        Ref("SerdePropertiesGrammar", optional=True),
+        Ref("StoredAsGrammar", optional=True),
+        Ref("LocationGrammar", optional=True),
+        Sequence(
+            OneOf(
+                Sequence(
+                    "CACHED",
+                    "IN",
+                    Delimited(Ref("PoolNameReferenceSegment")),
+                    Sequence(
+                        "WITH",
+                        "REPLICATION",
+                        "=",
+                        Ref("NumericLiteralSegment"),
+                        optional=True,
                     ),
                 ),
-                optional=True,
+                Ref.keyword("UNCACHED"),
             ),
-            Sequence(
-                "SORT",
-                "BY",
-                Bracketed(Delimited(Sequence(Ref("ColumnReferenceSegment")))),
-                optional=True,
-            ),
-            Ref("CommentGrammar", optional=True),
-            Ref("RowFormatClauseSegment", optional=True),
-            Ref("SerdePropertiesGrammar", optional=True),
-            Ref("StoredAsGrammar", optional=True),
-            Ref("LocationGrammar", optional=True),
-            Sequence(
-                OneOf(
+            optional=True,
+        ),
+        Ref("TablePropertiesGrammar", optional=True),
+    )
+
+
+class CreateTableAsSelectStatementSegment(BaseSegment):
+    """A `CREATE TABLE ... AS SELECT ...` statement.
+
+    Full Apache Impala reference here:
+    https://impala.apache.org/docs/build/html/topics/impala_create_table.html
+
+    Unlike Hive, `AS SELECT ...` cannot be appended to any other SELECT statement,
+    so this is implemented as a separate segment.
+    """
+
+    type = "create_table_as_select_statement"
+
+    match_grammar = Sequence(
+        "CREATE",
+        Ref.keyword("EXTERNAL", optional=True),
+        "TABLE",
+        Ref("IfNotExistsGrammar", optional=True),
+        Ref("TableReferenceSegment"),
+        Sequence(
+            "PARTITIONED",
+            "BY",
+            Bracketed(
+                Delimited(
                     Sequence(
-                        "CACHED",
-                        "IN",
-                        Delimited(Ref("PoolNameReferenceSegment")),
-                        Sequence(
-                            "WITH",
-                            "REPLICATION",
-                            "=",
-                            Ref("NumericLiteralSegment"),
-                            optional=True,
+                        OneOf(
+                            Ref("ColumnDefinitionSegment"),
+                            Ref("SingleIdentifierGrammar"),
                         ),
+                        Ref("CommentGrammar", optional=True),
+                    ),
+                ),
+            ),
+            optional=True,
+        ),
+        Sequence(
+            "SORT",
+            "BY",
+            Bracketed(Delimited(Sequence(Ref("ColumnReferenceSegment")))),
+            optional=True,
+        ),
+        Ref("CommentGrammar", optional=True),
+        Ref("RowFormatClauseSegment", optional=True),
+        Ref("SerdePropertiesGrammar", optional=True),
+        Ref("StoredAsGrammar", optional=True),
+        Ref("LocationGrammar", optional=True),
+        Sequence(
+            OneOf(
+                Sequence(
+                    "CACHED",
+                    "IN",
+                    Delimited(Ref("PoolNameReferenceSegment")),
+                    Sequence(
+                        "WITH",
+                        "REPLICATION",
+                        "=",
+                        Ref("NumericLiteralSegment"),
+                        optional=True,
                     ),
-                    Ref.keyword("UNCACHED"),
                 ),
-                optional=True,
+                Ref.keyword("UNCACHED"),
             ),
-            Ref("TablePropertiesGrammar", optional=True),
+            optional=True,
         ),
+        Ref("TablePropertiesGrammar", optional=True),
+        "AS",
+        Ref("SelectableGrammar"),
     )
 
 

Test patch: diff --git a/test/fixtures/dialects/impala/create_table_as_select.sql b/test/fixtures/dialects/impala/create_table_as_select.sql
new file mode 100644
index 00000000000..55d0830f316
--- /dev/null
+++ b/test/fixtures/dialects/impala/create_table_as_select.sql
@@ -0,0 +1,20 @@
+CREATE TABLE db.foo
+AS SELECT col1, col2
+FROM db.foo1;
+
+CREATE TABLE db.foo
+AS SELECT (col1, col2)
+FROM db.foo1;
+
+CREATE EXTERNAL TABLE IF NOT EXISTS db.foo
+    PARTITIONED BY (col1)
+    SORT BY (col2)
+    COMMENT 'table_comment'
+    ROW FORMAT DELIMITED
+    WITH SERDEPROPERTIES ('key1'='value1', 'key2'='value2')
+    STORED AS PARQUET
+    LOCATION 'hdfs://host/path/to/location'
+    TBLPROPERTIES ('key1'='value1', 'key2'='value2')
+AS
+    SELECT col1, col2, col3, col4
+    FROM db.baz;
diff --git a/test/fixtures/dialects/impala/create_table_as_select.yml b/test/fixtures/dialects/impala/create_table_as_select.yml
new file mode 100644
index 00000000000..11456b9f11f
--- /dev/null
+++ b/test/fixtures/dialects/impala/create_table_as_select.yml
@@ -0,0 +1,160 @@
+# 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: 08db1c90117ac7e42e905f533d92262860c54d61abf9b2d0de23467e9a4372fd
+file:
+- statement:
+    create_table_as_select_statement:
+    - keyword: CREATE
+    - keyword: TABLE
+    - table_reference:
+      - naked_identifier: db
+      - dot: .
+      - naked_identifier: foo
+    - keyword: AS
+    - select_statement:
+        select_clause:
+        - keyword: SELECT
+        - select_clause_element:
+            column_reference:
+              naked_identifier: col1
+        - comma: ','
+        - select_clause_element:
+            column_reference:
+              naked_identifier: col2
+        from_clause:
+          keyword: FROM
+          from_expression:
+            from_expression_element:
+              table_expression:
+                table_reference:
+                - naked_identifier: db
+                - dot: .
+                - naked_identifier: foo1
+- statement_terminator: ;
+- statement:
+    create_table_as_select_statement:
+    - keyword: CREATE
+    - keyword: TABLE
+    - table_reference:
+      - naked_identifier: db
+      - dot: .
+      - naked_identifier: foo
+    - keyword: AS
+    - select_statement:
+        select_clause:
+          keyword: SELECT
+          select_clause_element:
+            expression:
+              bracketed:
+              - start_bracket: (
+              - column_reference:
+                  naked_identifier: col1
+              - comma: ','
+              - column_reference:
+                  naked_identifier: col2
+              - end_bracket: )
+        from_clause:
+          keyword: FROM
+          from_expression:
+            from_expression_element:
+              table_expression:
+                table_reference:
+                - naked_identifier: db
+                - dot: .
+                - naked_identifier: foo1
+- statement_terminator: ;
+- statement:
+    create_table_as_select_statement:
+    - keyword: CREATE
+    - keyword: EXTERNAL
+    - keyword: TABLE
+    - keyword: IF
+    - keyword: NOT
+    - keyword: EXISTS
+    - table_reference:
+      - naked_identifier: db
+      - dot: .
+      - naked_identifier: foo
+    - keyword: PARTITIONED
+    - keyword: BY
+    - bracketed:
+        start_bracket: (
+        naked_identifier: col1
+        end_bracket: )
+    - keyword: SORT
+    - keyword: BY
+    - bracketed:
+        start_bracket: (
+        column_reference:
+          naked_identifier: col2
+        end_bracket: )
+    - keyword: COMMENT
+    - quoted_literal: "'table_comment'"
+    - row_format_clause:
+      - keyword: ROW
+      - keyword: FORMAT
+      - keyword: DELIMITED
+    - keyword: WITH
+    - keyword: SERDEPROPERTIES
+    - bracketed:
+      - start_bracket: (
+      - quoted_literal: "'key1'"
+      - comparison_operator:
+          raw_comparison_operator: '='
+      - quoted_literal: "'value1'"
+      - comma: ','
+      - quoted_literal: "'key2'"
+      - comparison_operator:
+          raw_comparison_operator: '='
+      - quoted_literal: "'value2'"
+      - end_bracket: )
+    - keyword: STORED
+    - keyword: AS
+    - keyword: PARQUET
+    - keyword: LOCATION
+    - quoted_literal: "'hdfs://host/path/to/location'"
+    - keyword: TBLPROPERTIES
+    - bracketed:
+      - start_bracket: (
+      - quoted_literal: "'key1'"
+      - comparison_operator:
+          raw_comparison_operator: '='
+      - quoted_literal: "'value1'"
+      - comma: ','
+      - quoted_literal: "'key2'"
+      - comparison_operator:
+          raw_comparison_operator: '='
+      - quoted_literal: "'value2'"
+      - end_bracket: )
+    - keyword: AS
+    - select_statement:
+        select_clause:
+        - keyword: SELECT
+        - select_clause_element:
+            column_reference:
+              naked_identifier: col1
+        - comma: ','
+        - select_clause_element:
+            column_reference:
+              naked_identifier: col2
+        - comma: ','
+        - select_clause_element:
+            column_reference:
+              naked_identifier: col3
+        - comma: ','
+        - select_clause_element:
+            column_reference:
+              naked_identifier: col4
+        from_clause:
+          keyword: FROM
+          from_expression:
+            from_expression_element:
+              table_expression:
+                table_reference:
+                - naked_identifier: db
+                - dot: .
+                - naked_identifier: baz
+- statement_terminator: ;