Repository: sqlfluff/sqlfluff

Issue:
BigQuery: Accessing `STRUCT` elements evades triggering L027
### Search before asking

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


### What Happened

Accessing unreferenced `STRUCT` elements using BigQuery dot notation in a multi table query does not trigger L027.

### Expected Behaviour

L027 gets triggered.

### Observed Behaviour

L027 does not get triggered.

### How to reproduce

```sql
SELECT
    t1.col1,
    t2.col2,
    events.id
FROM t_table1 AS t1
LEFT JOIN t_table2 AS t2
    ON TRUE
```

### Dialect

BigQUery

### Version

`0.11.2` using online.sqlfluff.com

### Configuration

N/A

### Are you willing to work on and submit a PR to address the issue?

- [ ] 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)



Discussion:
This is tricky.

Basicaly L026 works to make sure qualified columns only use tables in the from clause. This doesn’t really work for `STRUCT`s as impossible to know if it’s a qualified column or a `STRUCT`, so is off by default for languages that support them - like BigQuery.

L027 works to make sure columns are qualified for multi-table joins (i.e. have at least one dot). But it doesn’t check the qualifiers are valid - that’s L026’s job, which as I say is off by default for BigQuery.

Patch:
diff --git a/src/sqlfluff/rules/L027.py b/src/sqlfluff/rules/L027.py
--- a/src/sqlfluff/rules/L027.py
+++ b/src/sqlfluff/rules/L027.py
@@ -73,4 +73,21 @@ def _lint_references_and_aliases(
                     )
                 )
 
+            all_table_aliases = [t.ref_str for t in table_aliases] + standalone_aliases
+
+            # For qualified references, we want to check that the alias is actually
+            # valid
+            if (
+                this_ref_type == "qualified"
+                and list(r.iter_raw_references())[0].part not in all_table_aliases
+            ):
+                violation_buff.append(
+                    LintResult(
+                        anchor=r,
+                        description=f"Qualified reference {r.raw!r} not found in "
+                        f"available tables/view aliases {all_table_aliases} in select "
+                        "with more than one referenced table/view.",
+                    )
+                )
+
         return violation_buff or None


Test patch:
diff --git a/test/fixtures/rules/std_rule_cases/L027.yml b/test/fixtures/rules/std_rule_cases/L027.yml
--- a/test/fixtures/rules/std_rule_cases/L027.yml
+++ b/test/fixtures/rules/std_rule_cases/L027.yml
@@ -220,3 +220,40 @@ test_pass_rowtype_with_join:
   configs:
     core:
       dialect: hive
+
+test_fail_column_name_not_found_in_table_aliases_bigquery:
+  # qualified reference should actually exists in table aliases
+  fail_str: |
+    SELECT
+        a.bar,
+        b.foo,
+        this_is.some_struct.id
+    FROM
+        a LEFT JOIN b ON TRUE
+  configs:
+    core:
+      dialect: bigquery
+
+test_pass_column_name_is_a_struct_bigquery:
+  # check structs work as expected
+  pass_str: |
+    SELECT
+        a.bar,
+        b.this_is.some_struct.id
+    FROM
+        a LEFT JOIN b ON TRUE
+  configs:
+    core:
+      dialect: bigquery
+
+test_pass_column_name_from_unnest_bigquery:
+  # Check that we allow an table alias come from UNNEST statement
+  pass_str: |
+    SELECT
+        a.bar,
+        e.foo
+    FROM
+        a LEFT JOIN UNEST(a.events) AS e
+  configs:
+    core:
+      dialect: bigquery
diff --git a/test/rules/std_test.py b/test/rules/std_test.py
--- a/test/rules/std_test.py
+++ b/test/rules/std_test.py
@@ -68,7 +68,7 @@
         ),
         ("L016", "block_comment_errors_2.sql", [(1, 85), (2, 86)]),
         # Column references
-        ("L027", "column_references.sql", [(1, 8)]),
+        ("L027", "column_references.sql", [(1, 8), (1, 11)]),
         ("L027", "column_references_bare_function.sql", []),
         ("L026", "column_references.sql", [(1, 11)]),
         ("L025", "column_references.sql", [(2, 11)]),