Repository: sqlfluff/sqlfluff

Issue:
Support for DBT's `this.identifier`
### Search before asking

- [X] I searched the [issues](https://github.com/sqlfluff/sqlfluff/issues) and found no similar issues.


### Description

As can be seen in `jinja.py` ([here](https://github.com/sqlfluff/sqlfluff/blob/main/src/sqlfluff/core/templaters/jinja.py#L300)), `ThisEmulator` only supports `name`, `schema`, and `database`. But it should also support `identifier`, as documented [here](https://docs.getdbt.com/reference/resource-properties/identifier), as well as `type`, `is_table`, `is_view`, `is_cte`, and `include()`, as documented [here](https://docs.getdbt.com/reference/dbt-classes#creating-relations). 

Basically, `ref()`, `source()`, and `this` all return a Relation object.

![image](https://github.com/user-attachments/assets/ed1c745f-7606-4c67-a499-18d26e4233a4)

### Use case

Not get SQLFluff errors when using `this.identifier`.

### Dialect

Jinja/DBT

### 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)


Discussion:
mroy-seedbox: Also changed the return of `ref()` and `source()` to a `RelationEmulator`.

### Sample results
```python
t = ThisEmulator("ref_model")
print(t)
print(t.database)
print(t.schema)
print(t.name)
print(t.identifier)
print(t.type)
print(t.something_new)
print(t.is_table)
print(t.is_view)
print(t.is_materialized_view)
print(t.is_cte)
print(t.is_dynamic_table)
print(t.is_iceberg_format)
print(t.include())
print(t.include(database=False))
print(t.some_new_method())
```

Prints:
```
ref_model
this_database
this_schema
ref_model
ref_model
ref_model
ref_model
True
True
True
True
True
True
ref_model
ref_model
ref_model
```
mroy-seedbox: @alanmcruickshank: I fixed a few things! The next test run should work better. 🤞 
mroy-seedbox: Ah crap.... `typing.Self` was only added in Python 3.11. 🤦‍♂️ 
mroy-seedbox: @alanmcruickshank: Fixed!
mroy-seedbox: Thanks for the quick fix! 🙌 

Not sure why it can't find `typing_extensions`... I added it to [requirements_dev.txt](https://github.com/sqlfluff/sqlfluff/pull/6254/files#diff-c9c796259f3852b51b531b79cbf07820145088d4f108fb006ab44b15f0b15934). 🤷‍♂️ 
github-actions[bot]: # Coverage Results ✅
```
Name    Stmts   Miss  Cover   Missing
-------------------------------------
TOTAL   18358      0   100%

237 files skipped due to complete coverage.

```

mroy-seedbox: Ah, duh.... it's not just a dev dependency. 🤦‍♂️ 
coveralls: 
[![Coverage Status](https://coveralls.io/builds/70024936/badge)](https://coveralls.io/builds/70024936)

coverage: 99.985%. remained the same
when pulling **ac490874eb7529f0cfb9cb7f19bb05a83f233a00 on mroy-seedbox:main**
into **6dd6433a51e87d2ca421260f3c2b3ea4ef5dac01 on sqlfluff:main**.

mroy-seedbox: @alanmcruickshank: Just added `typing-extensions` to `pyproject.toml`, so that should fix the remaining issues!
alanmcruickshank: > @alanmcruickshank: Just added `typing-extensions` to `pyproject.toml`, so that should fix the remaining issues!

I'm not sure you should need to add `typing-extensions`, I've been wrestling a lot with typing recently so have a suggestion on that front. I've also removed the `# pragma: no cover` comments, because I think we should try and get coverage on those methods.
mroy-seedbox: @alanmcruickshank: Tests are ready!
mroy-seedbox: @alanmcruickshank: Just need to run the test coverage on the latest commit, and it should pass! 👌 
mroy-seedbox: Actually, just checking this now, and there's a little improvement that could be made to conform better to how DBT Relations work!

Won't take long!
mroy-seedbox: The full test suite is still running on my end, but everything is looking good so far!
mroy-seedbox: @alanmcruickshank: Alright, all good on my end! ✅  Although I can't get the coverage report to complete. It says "No data to combine". 🤷‍♂️ 

Apologies for my laziness & lack of time/effort earlier in this process. 😞 

Patch:
diff --git a/src/sqlfluff/core/templaters/jinja.py b/src/sqlfluff/core/templaters/jinja.py
index 95bf05d73..8066ddbce 100644
--- a/src/sqlfluff/core/templaters/jinja.py
+++ b/src/sqlfluff/core/templaters/jinja.py
@@ -314,21 +314,34 @@ class JinjaTemplater(PythonTemplater):
         # be configurable somewhere sensible. But for now they're not.
         # TODO: Come up with a better solution.
 
-        class ThisEmulator:
+        class RelationEmulator:
             """A class which emulates the `this` class from dbt."""
 
-            name = "this_model"
+            identifier = "this_model"
             schema = "this_schema"
             database = "this_database"
 
-            def __str__(self) -> str:  # pragma: no cover TODO?
-                return self.name
+            def __init__(self, identifier: str = "this_model") -> None:
+                self.identifier = identifier
+
+            def __call__(self, *args: Any, **kwargs: Any) -> "RelationEmulator":
+                return self
+
+            def __getattr__(self, name: str) -> Union["RelationEmulator", bool]:
+                if name[0:3] == "is_":
+                    return True
+                return self
+
+            def __str__(self) -> str:
+                return self.identifier
 
         dbt_builtins = {
-            "ref": lambda *args: args[-1],
+            "ref": lambda *args, **kwargs: RelationEmulator(args[-1]),
             # In case of a cross project ref in dbt, model_ref is the second
             # argument. Otherwise it is the only argument.
-            "source": lambda source_name, table: f"{source_name}_{table}",
+            "source": lambda source_name, table: RelationEmulator(
+                f"{source_name}_{table}"
+            ),
             "config": lambda **kwargs: "",
             "var": lambda variable, default="": "item",
             # `is_incremental()` renders as True, always in this case.
@@ -337,7 +350,7 @@ class JinjaTemplater(PythonTemplater):
             # We should try to find a solution to that. Perhaps forcing the file
             # to be parsed TWICE if it uses this variable.
             "is_incremental": lambda: True,
-            "this": ThisEmulator(),
+            "this": RelationEmulator(),
         }
         return dbt_builtins
 
diff --git a/src/sqlfluff/rules/references/RF05.py b/src/sqlfluff/rules/references/RF05.py
index d0c1ac9ff..6ea99e915 100644
--- a/src/sqlfluff/rules/references/RF05.py
+++ b/src/sqlfluff/rules/references/RF05.py
@@ -107,7 +107,7 @@ class Rule_RF05(BaseRule):
             policy = self.quoted_identifiers_policy
 
             # Strip the quotes first
-            identifier = context.segment.raw[1:-1]
+            identifier = context.segment.raw_normalized(casefold=False)
 
             # Skip if in ignore list - repeat check now we've strip the quotes
             if ignore_words_list and identifier.lower() in ignore_words_list:
diff --git a/src/sqlfluff/rules/references/__init__.py b/src/sqlfluff/rules/references/__init__.py
index 41201beda..800bbc80d 100644
--- a/src/sqlfluff/rules/references/__init__.py
+++ b/src/sqlfluff/rules/references/__init__.py
@@ -15,11 +15,11 @@ def get_configs_info() -> Dict[str, Any]:
             "definition": "The expectation for references in single-table select.",
         },
         "unquoted_identifiers_policy": {
-            "validation": ["all", "aliases", "column_aliases"],
+            "validation": ["all", "aliases", "column_aliases", "table_aliases"],
             "definition": "Types of unquoted identifiers to flag violations for.",
         },
         "quoted_identifiers_policy": {
-            "validation": ["all", "aliases", "column_aliases", "none"],
+            "validation": ["all", "aliases", "column_aliases", "table_aliases", "none"],
             "definition": "Types of quoted identifiers to flag violations for.",
         },
         "allow_space_in_identifier": {
diff --git a/src/sqlfluff/utils/identifers.py b/src/sqlfluff/utils/identifers.py
index 0357be53f..964954f45 100644
--- a/src/sqlfluff/utils/identifers.py
+++ b/src/sqlfluff/utils/identifers.py
@@ -29,4 +29,6 @@ def identifiers_policy_applicable(
     is_inside_from = any(p.is_type("from_clause") for p in parent_stack)
     if policy == "column_aliases" and is_alias and not is_inside_from:
         return True
+    if policy == "table_aliases" and is_alias and is_inside_from:
+        return True
     return False


Test patch:
diff --git a/test/core/templaters/jinja_test.py b/test/core/templaters/jinja_test.py
index 9fccefce2..9c6a722e5 100644
--- a/test/core/templaters/jinja_test.py
+++ b/test/core/templaters/jinja_test.py
@@ -1790,6 +1790,161 @@ def test_undefined_magic_methods():
     assert ud + ud is ud
 
 
+def test_relation_emulator_magic_methods():
+    """Test all the magic methods defined on RelationEmulator."""
+    dbt_builtins = JinjaTemplater._generate_dbt_builtins()
+
+    # tests for 'this'
+    t = dbt_builtins["this"]
+    assert str(t) == "this_model"
+    assert t.something is t
+    assert str(t.database) == "this_database"
+    assert str(t.schema) == "this_schema"
+    assert str(t.name) == "this_model"
+    assert str(t.identifier) == "this_model"
+    assert str(t.type) == "this_model"
+    assert str(t.something_new) == "this_model"
+    assert t.is_table is True
+    assert t.is_view is True
+    assert t.is_materialized_view is True
+    assert t.is_cte is True
+    assert t.is_dynamic_table is True
+    assert t.is_iceberg_format is True
+    assert t.is_something_new is True
+    assert t.something() is t
+    assert t.something().something() is t
+    assert t.something().something is t
+    assert str(t.include()) == "this_model"
+    assert str(t.include(database=False)) == "this_model"
+    assert str(t.some_new_method()) == "this_model"
+    assert str(t.something().something) == "this_model"
+
+    # tests for 'ref'
+    r = dbt_builtins["ref"]("ref_model")
+    assert str(r) == "ref_model"
+    assert r.something is r
+    assert str(r.database) == "this_database"
+    assert str(r.schema) == "this_schema"
+    assert str(r.name) == "ref_model"
+    assert str(r.identifier) == "ref_model"
+    assert str(r.type) == "ref_model"
+    assert str(r.something_new) == "ref_model"
+    assert r.is_table is True
+    assert r.is_view is True
+    assert r.is_materialized_view is True
+    assert r.is_cte is True
+    assert r.is_dynamic_table is True
+    assert r.is_iceberg_format is True
+    assert r.is_something_new is True
+    assert r.something() is r
+    assert r.something().something() is r
+    assert r.something().something is r
+    assert str(r.include()) == "ref_model"
+    assert str(r.include(database=False)) == "ref_model"
+    assert str(r.some_new_method()) == "ref_model"
+    assert str(r.something().something) == "ref_model"
+
+    # tests for versioned 'ref'
+    r = dbt_builtins["ref"]("ref_model", version=2)
+    assert str(r) == "ref_model"
+    assert r.something is r
+    assert str(r.database) == "this_database"
+    assert str(r.schema) == "this_schema"
+    assert str(r.name) == "ref_model"
+    assert str(r.identifier) == "ref_model"
+    assert str(r.type) == "ref_model"
+    assert str(r.something_new) == "ref_model"
+    assert r.is_table is True
+    assert r.is_view is True
+    assert r.is_materialized_view is True
+    assert r.is_cte is True
+    assert r.is_dynamic_table is True
+    assert r.is_iceberg_format is True
+    assert r.is_something_new is True
+    assert r.something() is r
+    assert r.something().something() is r
+    assert r.something().something is r
+    assert str(r.include()) == "ref_model"
+    assert str(r.include(database=False)) == "ref_model"
+    assert str(r.some_new_method()) == "ref_model"
+    assert str(r.something().something) == "ref_model"
+
+    # tests for 'ref' from project/package
+    r = dbt_builtins["ref"]("package", "ref_model")
+    assert str(r) == "ref_model"
+    assert r.something is r
+    assert str(r.database) == "this_database"
+    assert str(r.schema) == "this_schema"
+    assert str(r.name) == "ref_model"
+    assert str(r.identifier) == "ref_model"
+    assert str(r.type) == "ref_model"
+    assert str(r.something_new) == "ref_model"
+    assert r.is_table is True
+    assert r.is_view is True
+    assert r.is_materialized_view is True
+    assert r.is_cte is True
+    assert r.is_dynamic_table is True
+    assert r.is_iceberg_format is True
+    assert r.is_something_new is True
+    assert r.something() is r
+    assert r.something().something() is r
+    assert r.something().something is r
+    assert str(r.include()) == "ref_model"
+    assert str(r.include(database=False)) == "ref_model"
+    assert str(r.some_new_method()) == "ref_model"
+    assert str(r.something().something) == "ref_model"
+
+    # tests for versioned 'ref' from project/package
+    r = dbt_builtins["ref"]("package", "ref_model", version=2)
+    assert str(r) == "ref_model"
+    assert r.something is r
+    assert str(r.database) == "this_database"
+    assert str(r.schema) == "this_schema"
+    assert str(r.name) == "ref_model"
+    assert str(r.identifier) == "ref_model"
+    assert str(r.type) == "ref_model"
+    assert str(r.something_new) == "ref_model"
+    assert r.is_table is True
+    assert r.is_view is True
+    assert r.is_materialized_view is True
+    assert r.is_cte is True
+    assert r.is_dynamic_table is True
+    assert r.is_iceberg_format is True
+    assert r.is_something_new is True
+    assert r.something() is r
+    assert r.something().something() is r
+    assert r.something().something is r
+    assert str(r.include()) == "ref_model"
+    assert str(r.include(database=False)) == "ref_model"
+    assert str(r.some_new_method()) == "ref_model"
+    assert str(r.something().something) == "ref_model"
+
+    # tests for 'source'
+    s = dbt_builtins["source"]("sourcename", "tablename")
+    assert str(s) == "sourcename_tablename"
+    assert s.something is s
+    assert str(s.database) == "this_database"
+    assert str(s.schema) == "this_schema"
+    assert str(s.name) == "sourcename_tablename"
+    assert str(s.identifier) == "sourcename_tablename"
+    assert str(s.type) == "sourcename_tablename"
+    assert str(s.something_new) == "sourcename_tablename"
+    assert s.is_table is True
+    assert s.is_view is True
+    assert s.is_materialized_view is True
+    assert s.is_cte is True
+    assert s.is_dynamic_table is True
+    assert s.is_iceberg_format is True
+    assert s.is_something_new is True
+    assert s.something() is s
+    assert s.something().something() is s
+    assert s.something().something is s
+    assert str(s.include()) == "sourcename_tablename"
+    assert str(s.include(database=False)) == "sourcename_tablename"
+    assert str(s.some_new_method()) == "sourcename_tablename"
+    assert str(s.something().something) == "sourcename_tablename"
+
+
 @pytest.mark.parametrize(
     "sql_path, expected_renderings",
     [
diff --git a/test/fixtures/rules/std_rule_cases/RF05.yml b/test/fixtures/rules/std_rule_cases/RF05.yml
index 4b54290f7..cb2976ef8 100644
--- a/test/fixtures/rules/std_rule_cases/RF05.yml
+++ b/test/fixtures/rules/std_rule_cases/RF05.yml
@@ -1,30 +1,26 @@
 rule: RF05
 
 test_fail_special_chars_create_table_space:
-  fail_str:
-    CREATE TABLE DBO.ColumnNames
-    (
+  fail_str: |
+    CREATE TABLE DBO.ColumnNames (
         "Internal Space" INT
     )
 
 test_fail_special_chars_create_table_gt:
-  fail_str:
-    CREATE TABLE DBO.ColumnNames
-    (
+  fail_str: |
+    CREATE TABLE DBO.ColumnNames (
         "Greater>Than" INT
     )
 
 test_fail_special_chars_create_table_lt:
-  fail_str:
-    CREATE TABLE DBO.ColumnNames
-    (
+  fail_str: |
+    CREATE TABLE DBO.ColumnNames (
         "Less<Than" INT
     )
 
 test_fail_special_chars_create_table_lt_bigquery:
-  fail_str:
-    CREATE TABLE DBO.ColumnNames
-    (
+  fail_str: |
+    CREATE TABLE DBO.ColumnNames (
         `Less<Than` INT
     )
   configs:
@@ -32,9 +28,8 @@ test_fail_special_chars_create_table_lt_bigquery:
       dialect: bigquery
 
 test_fail_special_chars_create_table_hash_unquoted:
-  fail_str:
-    CREATE TABLE DBO.ColumnNames
-    (
+  fail_str: |
+    CREATE TABLE DBO.ColumnNames (
         Number# INT
     )
   configs:
@@ -42,9 +37,8 @@ test_fail_special_chars_create_table_hash_unquoted:
       dialect: tsql
 
 test_pass_special_chars_create_table_none:
-  pass_str:
-    CREATE TABLE DBO.ColumnNames
-    (
+  pass_str: |
+    CREATE TABLE DBO.ColumnNames (
         "No_Internal_Space" INT,
         GreaterThan INT,
         LessThan INT,
@@ -52,9 +46,8 @@ test_pass_special_chars_create_table_none:
     )
 
 test_pass_special_chars_create_table_none_quoted_tsql:
-  pass_str:
-    CREATE TABLE DBO.ColumnNames
-    (
+  pass_str: |
+    CREATE TABLE DBO.ColumnNames (
         [No_Internal_Space] INT,
         [GreaterThan] INT,
         [LessThan] INT,
@@ -65,9 +58,8 @@ test_pass_special_chars_create_table_none_quoted_tsql:
       dialect: tsql
 
 test_pass_special_chars_create_table_none_quoted_biquery:
-  pass_str:
-    CREATE TABLE DBO.ColumnNames
-    (
+  pass_str: |
+    CREATE TABLE DBO.ColumnNames (
         `No_Internal_Space` INT,
         `GreaterThan` INT,
         `LessThan` INT,
@@ -78,9 +70,7 @@ test_pass_special_chars_create_table_none_quoted_biquery:
       dialect: bigquery
 
 test_pass_special_chars_create_table_space_allowed:
-  pass_str:
-    CREATE TABLE DBO.ColumnNames
-    (
+  pass_str: CREATE TABLE DBO.ColumnNames (
         "Internal Space" INT
     )
   configs:
@@ -89,7 +79,7 @@ test_pass_special_chars_create_table_space_allowed:
         allow_space_in_identifier: true
 
 test_fail_special_chars_quoted_policy_alias:
-  fail_str:
+  fail_str: |
     SELECT a
     FROM DBO.ColumnNames as "alias with space"
   configs:
@@ -98,7 +88,7 @@ test_fail_special_chars_quoted_policy_alias:
         quoted_identifiers_policy: aliases
 
 test_fail_special_chars_quoted_policy_column_alias:
-  fail_str:
+  fail_str: |
     SELECT a as "alias with space"
     FROM DBO.ColumnNames
   configs:
@@ -107,7 +97,7 @@ test_fail_special_chars_quoted_policy_column_alias:
         quoted_identifiers_policy: column_aliases
 
 test_fail_special_chars_unquoted_policy_aliases:
-  fail_str:
+  fail_str: |
     SELECT a
     FROM DBO.ColumnNames as aliashash#
   configs:
@@ -118,7 +108,7 @@ test_fail_special_chars_unquoted_policy_aliases:
       dialect: tsql
 
 test_fail_special_chars_unquoted_policy_column_aliases:
-  fail_str:
+  fail_str: |
     SELECT a as aliashash#
     FROM DBO.ColumnNames
   configs:
@@ -129,7 +119,7 @@ test_fail_special_chars_unquoted_policy_column_aliases:
       dialect: tsql
 
 test_pass_special_chars_quoted_policy_aliases:
-  pass_str:
+  pass_str: |
     SELECT "column with space" as a
     FROM DBO.ColumnNames
   configs:
@@ -138,7 +128,7 @@ test_pass_special_chars_quoted_policy_aliases:
         quoted_identifiers_policy: aliases
 
 test_pass_special_chars_quoted_policy_none:
-  pass_str:
+  pass_str: |
     SELECT "column with space" as "alias with space"
     FROM DBO.ColumnNames
   configs:
@@ -147,7 +137,7 @@ test_pass_special_chars_quoted_policy_none:
         quoted_identifiers_policy: none
 
 test_pass_special_chars_unquoted_policy_aliases:
-  pass_str:
+  pass_str: |
     SELECT Number# as a
     FROM DBO.ColumnNames
   configs:
@@ -158,7 +148,7 @@ test_pass_special_chars_unquoted_policy_aliases:
       dialect: tsql
 
 test_pass_special_chars_unquoted_policy_column_aliases:
-  pass_str:
+  pass_str: |
     SELECT Number# as a
     FROM DBO.ColumnNames as AliasHash#
   configs:
@@ -169,7 +159,7 @@ test_pass_special_chars_unquoted_policy_column_aliases:
       dialect: tsql
 
 test_pass_special_chars_quoted_policy_column_aliases:
-  pass_str:
+  pass_str: |
     SELECT "column with space" as a
     FROM DBO.ColumnNames as "alias with space"
   configs:
@@ -178,7 +168,7 @@ test_pass_special_chars_quoted_policy_column_aliases:
         quoted_identifiers_policy: column_aliases
 
 test_pass_dots_bigquery:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM `user.schema.table`
   configs:
@@ -186,7 +176,7 @@ test_pass_dots_bigquery:
       dialect: bigquery
 
 test_pass_star_bigquery:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM `user.schema.table_*`
   configs:
@@ -194,7 +184,7 @@ test_pass_star_bigquery:
       dialect: bigquery
 
 test_fail_star_bigquery:
-  fail_str:
+  fail_str: |
     SELECT a
     FROM `user.schema.table_*_something`
   configs:
@@ -204,7 +194,7 @@ test_fail_star_bigquery:
 test_pass_hyphen_bigquery_quotes_1:
   # Passes without setting 'additional_allowed_characters' because RF05 allows
   # hyphens in BigQuery by default.
-  pass_str:
+  pass_str: |
     SELECT a
     FROM `user.schema.table-something`
   configs:
@@ -214,7 +204,7 @@ test_pass_hyphen_bigquery_quotes_1:
 test_pass_hyphen_bigquery_quotes_2:
   # Passes because explicitly setting 'additional_allowed_characters' adds to the
   # default ("-") value.
-  pass_str:
+  pass_str: |
     SELECT a
     FROM `user.schema.table-some+thing`
   configs:
@@ -222,10 +212,10 @@ test_pass_hyphen_bigquery_quotes_2:
       dialect: bigquery
     rules:
       references.special_chars:
-        additional_allowed_characters: '+'
+        additional_allowed_characters: "+"
 
 test_pass_hyphen_bigquery_no_quotes:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM user.schema.table-something
   configs:
@@ -233,7 +223,7 @@ test_pass_hyphen_bigquery_no_quotes:
       dialect: bigquery
 
 test_pass_dot_bigquery:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM `user.schema.table-something`
     WHERE `user.schema.table-something`.a = 1
@@ -242,10 +232,10 @@ test_pass_dot_bigquery:
       dialect: bigquery
     rules:
       references.special_chars:
-        additional_allowed_characters: '-.'
+        additional_allowed_characters: "-."
 
 test_fail_single_quote_bigquery:
-  fail_str:
+  fail_str: |
     SELECT a
     FROM `user.schema.table'something`
   configs:
@@ -253,10 +243,10 @@ test_fail_single_quote_bigquery:
       dialect: bigquery
     rules:
       references.special_chars:
-        additional_allowed_characters: '-.'
+        additional_allowed_characters: "-."
 
 test_pass_single_quote_bigquery:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM `user.schema.table'something`
   configs:
@@ -264,10 +254,10 @@ test_pass_single_quote_bigquery:
       dialect: bigquery
     rules:
       references.special_chars:
-        additional_allowed_characters: '-''.'
+        additional_allowed_characters: "-'."
 
 test_pass_single_quote2_bigquery:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM `user.schema.table'something`
   configs:
@@ -278,7 +268,7 @@ test_pass_single_quote2_bigquery:
         additional_allowed_characters: "-'."
 
 test_pass_dot_slash_identifier_in_file_reference_sparksql:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM TEXT.`examples/src/main/resources/people.txt`
   configs:
@@ -286,7 +276,7 @@ test_pass_dot_slash_identifier_in_file_reference_sparksql:
       dialect: sparksql
 
 test_pass_star_identifier_in_file_reference_sparksql:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM TEXT.`//root/*.txt`;
   configs:
@@ -294,7 +284,7 @@ test_pass_star_identifier_in_file_reference_sparksql:
       dialect: sparksql
 
 test_pass_question_mark_identifier_in_file_reference_sparksql:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM TEXT.`//root/200?.txt`;
   configs:
@@ -302,7 +292,7 @@ test_pass_question_mark_identifier_in_file_reference_sparksql:
       dialect: sparksql
 
 test_pass_character_class_identifier_in_file_reference_sparksql:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM TEXT.`//root/200[23].txt`;
   configs:
@@ -310,7 +300,7 @@ test_pass_character_class_identifier_in_file_reference_sparksql:
       dialect: sparksql
 
 test_pass_negated_character_class_identifier_in_file_reference_sparksql:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM TEXT.`//root/200[^23].txt`;
   configs:
@@ -318,7 +308,7 @@ test_pass_negated_character_class_identifier_in_file_reference_sparksql:
       dialect: sparksql
 
 test_pass_character_range_identifier_in_file_reference_sparksql:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM TEXT.`//root/200[2-5].txt`;
   configs:
@@ -326,7 +316,7 @@ test_pass_character_range_identifier_in_file_reference_sparksql:
       dialect: sparksql
 
 test_pass_negated_character_range_identifier_in_file_reference_sparksql:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM TEXT.`//root/200[^2-5].txt`;
   configs:
@@ -334,7 +324,7 @@ test_pass_negated_character_range_identifier_in_file_reference_sparksql:
       dialect: sparksql
 
 test_pass_alteration_identifier_in_file_reference_sparksql:
-  pass_str:
+  pass_str: |
     SELECT a
     FROM TEXT.`//root/20{04, 05}.txt`;
   configs:
@@ -342,7 +332,7 @@ test_pass_alteration_identifier_in_file_reference_sparksql:
       dialect: sparksql
 
 test_pass_ignore_lists_quoted:
-  pass_str:
+  pass_str: |
     SELECT a as 'aliashash#'
   configs:
     rules:
@@ -350,20 +340,17 @@ test_pass_ignore_lists_quoted:
         ignore_words: aliashash#
 
 test_pass_ignore_lists_quoted_fail:
-  fail_str:
-    SELECT a as 'aliashash#'
+  fail_str: SELECT a as 'aliashash#'
 
 test_pass_ignore_lists_quoted_mixed_case:
-  pass_str:
-    SELECT a as 'aliasHash#'
+  pass_str: SELECT a as 'aliasHash#'
   configs:
     rules:
       references.special_chars:
         ignore_words: aliashash#
 
 test_pass_ignore_lists_unquoted:
-  pass_str:
-    SELECT a as alias$
+  pass_str: SELECT a as alias$
   configs:
     core:
       dialect: postgres
@@ -372,15 +359,13 @@ test_pass_ignore_lists_unquoted:
         ignore_words: alias$
 
 test_pass_ignore_lists_unquoted_fail:
-  fail_str:
-    SELECT a as alias$
+  fail_str: SELECT a as alias$
   configs:
     core:
       dialect: postgres
 
 test_pass_ignore_lists_unquoted_mixed_case:
-  pass_str:
-    SELECT a as Alias$
+  pass_str: SELECT a as Alias$
   configs:
     core:
       dialect: postgres
@@ -389,8 +374,7 @@ test_pass_ignore_lists_unquoted_mixed_case:
         ignore_words: alias$
 
 test_pass_ignore_words_regex_unquoted:
-  pass_str:
-    SELECT a as alias$
+  pass_str: SELECT a as alias$
   configs:
     core:
       dialect: postgres
@@ -399,31 +383,27 @@ test_pass_ignore_words_regex_unquoted:
         ignore_words_regex: lias\$$
 
 test_pass_ignore_words_regex_quoted:
-  pass_str:
-    SELECT a as 'aliashash#'
+  pass_str: SELECT a as 'aliashash#'
   configs:
     rules:
       references.special_chars:
         ignore_words_regex: hash#$
 
 test_fail_ignore_words_regex_quoted_mixed_case:
-  fail_str:
-    SELECT a as 'aliasHash#'
+  fail_str: SELECT a as 'aliasHash#'
   configs:
     rules:
       references.special_chars:
         ignore_words_regex: hash#$
 
 test_pass_special_chars_show_tblproperties:
-  pass_str:
-    SHOW TBLPROPERTIES customer ('created.date');
+  pass_str: SHOW TBLPROPERTIES customer ('created.date');
   configs:
     core:
       dialect: sparksql
 
 test_fail_special_chars_show_tblproperties:
-  fail_str:
-    SHOW TBLPROPERTIES customer ('created.*');
+  fail_str: SHOW TBLPROPERTIES customer ('created.*');
   configs:
     core:
       dialect: sparksql
@@ -446,3 +426,21 @@ test_fail_special_chars_redshift_hash_column:
   configs:
     core:
       dialect: redshift
+
+test_pass_table_aliases:
+  pass_str: |
+    SELECT 1 AS "special*"
+    FROM VALUES ((1)) AS "non_special"
+  configs:
+    rules:
+      references.special_chars:
+        quoted_identifiers_policy: table_aliases
+
+test_fail_table_aliases:
+  fail_str: |
+    SELECT 1 AS "special*"
+    FROM VALUES ((1)) AS "special*"
+  configs:
+    rules:
+      references.special_chars:
+        quoted_identifiers_policy: table_aliases