Repository: sqlfluff/sqlfluff
Issue category: Feature Request

Improve inline directives for templater context
### Search before asking

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


### Description

I cannot alter jinja context in inline fashion.

Good:

```
[sqlfluff:templater:jinja:context]
my_inline_dict={"k1": "v1", "k2": "v2"}
```

SQL:
```sql
{% for k in my_inline_dict %}
SELECT {{ k }};
{% endfor %}
```

sqlfluff render:
```sql
SELECT k1;

SELECT k2;
```

Bad:
```sql
-- sqlfluff:templater:jinja:context:my_inline_dict:{"k1": "v1", "k2": "v2"}
{% for k in my_inline_dict %}
SELECT {{ k }};
{% endfor %}
```

sqlfluff render:
```
Traceback (most recent call last):
  File "/home/lrogalski/repo/REDACTED/.venv/bin/sqlfluff", line 8, in <module>
    sys.exit(cli())
             ^^^^^
  File "/home/lrogalski/repo/REDACTED/.venv/lib/python3.11/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/lrogalski/repo/REDACTED/.venv/lib/python3.11/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/home/lrogalski/repo/REDACTED/.venv/lib/python3.11/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/lrogalski/repo/REDACTED/.venv/lib/python3.11/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/lrogalski/repo/REDACTED/.venv/lib/python3.11/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/lrogalski/repo/REDACTED/.venv/lib/python3.11/site-packages/sqlfluff/cli/commands.py", line 1451, in render
    raw_sql, file_config, _ = lnt.load_raw_file_and_config(path, lnt.config)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/lrogalski/repo/REDACTED/.venv/lib/python3.11/site-packages/sqlfluff/core/linter/linter.py", line 147, in load_raw_file_and_config
    file_config.process_raw_file_for_config(raw_file, fname)
  File "/home/lrogalski/repo/REDACTED/.venv/lib/python3.11/site-packages/sqlfluff/core/config.py", line 1164, in process_raw_file_for_config
    self.process_inline_config(raw_line, fname)
  File "/home/lrogalski/repo/REDACTED/.venv/lib/python3.11/site-packages/sqlfluff/core/config.py", line 1150, in process_inline_config
    self.set_value(*config_val)
  File "/home/lrogalski/repo/REDACTED/.venv/lib/python3.11/site-packages/sqlfluff/core/config.py", line 1094, in set_value
    dict_buff.append(dict_buff[-1].get(elem, {}))
                     ^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'get'
```

3.2.4

### Use case

I want to have "local" jinja context in my sql to:
- improve readability
- avoid always-growing and self-conflicting context in .sqlfluff file

### Dialect

ansi

### 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/core/helpers/string.py:split_colon_separated_string
Functions added: src/sqlfluff/core/helpers/string.py:should_split_on_colon

Patch:
diff --git a/src/sqlfluff/core/helpers/string.py b/src/sqlfluff/core/helpers/string.py
index 85d21b0bf56..c425e9cc612 100644
--- a/src/sqlfluff/core/helpers/string.py
+++ b/src/sqlfluff/core/helpers/string.py
@@ -38,31 +38,45 @@ def split_colon_separated_string(in_str: str) -> Tuple[Tuple[str, ...], str]:
     >>> split_colon_separated_string("a")
     ((), 'a')
 
-    NOTE: This also includes some provisions for values which may be
-    Windows paths containing colons and NOT stripping those.
+    NOTE: This also includes some heuristics for legit values containing colon.
     >>> split_colon_separated_string("foo:bar:C:\\Users")
     (('foo', 'bar'), 'C:\\Users')
+    >>> split_colon_separated_string('foo:bar:{"k":"v"}')
+    (('foo', 'bar'), '{"k":"v"}')
+    >>> split_colon_separated_string('foo:bar:[{"k":"v"}]')
+    (('foo', 'bar'), '[{"k":"v"}]')
     """
     config_path: List[str] = []
-    for element in in_str.split(":"):
-        # If the next element begins with a backslash, and the previous
-        # one had length == 1,  then this is probably a windows path.
-        # In which case, rejoin them together.
+    leftover = in_str
+    while ":" in leftover:
+        element, _, value = leftover.partition(":")
         element = element.strip()
-        if (
-            element
-            and element[0] == "\\"
-            and config_path[-1]
-            and len(config_path[-1]) == 1
-        ):
-            config_path[-1] = config_path[-1] + ":" + element
-        else:
-            # Otherwise just add it to the path.
-            config_path.append(element)
-
+        value = value.strip()
+        config_path.append(element)
+        leftover = value
+        if not should_split_on_colon(value):
+            break
+
+    # last part - actual value
+    config_path.append(leftover)
     return tuple(config_path[:-1]), config_path[-1]
 
 
+def should_split_on_colon(value: str) -> bool:
+    """Heuristic for legit values containing comma."""
+    if len(value) >= 2 and value[1] == ":" and value[2] == "\\":
+        # Likely a Windows path
+        return False
+    if len(value) >= 2 and (value[0] == "[" and value[-1] == "]"):
+        # Likely a JSON array
+        return False
+    if len(value) >= 2 and (value[0] == "{" and value[-1] == "}"):
+        # Likely a JSON object
+        return False
+
+    return True
+
+
 def split_comma_separated_string(raw: Union[str, List[str]]) -> List[str]:
     """Converts comma separated string to List, stripping whitespace."""
     if isinstance(raw, str):

Test patch: diff --git a/test/core/config/fluffconfig_test.py b/test/core/config/fluffconfig_test.py
index 35b0619b546..bcd67dda9d4 100644
--- a/test/core/config/fluffconfig_test.py
+++ b/test/core/config/fluffconfig_test.py
@@ -340,6 +340,14 @@ def test__process_inline_config():
     cfg.process_inline_config("-- sqlfluff:jinja:my_path:c:\\foo", "test.sql")
     assert cfg.get("my_path", section="jinja") == "c:\\foo"
 
+    # Check that JSON objects are not mangled
+    cfg.process_inline_config('-- sqlfluff:jinja:my_dict:{"k":"v"}', "test.sql")
+    assert cfg.get("my_dict", section="jinja") == '{"k":"v"}'
+
+    # Check that JSON arrays are not mangled
+    cfg.process_inline_config('-- sqlfluff:jinja:my_dict:[{"k":"v"}]', "test.sql")
+    assert cfg.get("my_dict", section="jinja") == '[{"k":"v"}]'
+
 
 @pytest.mark.parametrize(
     "raw_sql",