Repository: alanmcruickshank/sqlfluff

Issue:
Bug: fix ValueError: Unexpected capitalisation policy: 'inconsistent'
I don't even know what is happening here!

```
➜ sqlfluff version                                                                                   
0.2.4

➜ echo 'selECT * from table;' > test.sql

➜ sqlfluff fix test.sql --rules L001,L002,L003,L004,L005,L006,L007,L008,L009,L010,L011,L012,L013,L014
==== finding violations ====
Traceback (most recent call last):
  File "/Users/nolan/anaconda3/bin/sqlfluff", line 10, in <module>
    sys.exit(cli())
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/cli/commands.py", line 174, in fix
    result = lnt.lint_paths(paths, verbosity=verbose)
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/linter.py", line 605, in lint_paths
    result.add(self.lint_path(path, verbosity=verbosity, fix=fix))
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/linter.py", line 592, in lint_path
    fix=fix, config=config))
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/linter.py", line 536, in lint_string
    lerrs, _, _, _ = crawler.crawl(parsed)
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/rules/base.py", line 192, in crawl
    raw_stack=raw_stack, fix=fix, memory=memory)
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/rules/base.py", line 192, in crawl
    raw_stack=raw_stack, fix=fix, memory=memory)
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/rules/base.py", line 192, in crawl
    raw_stack=raw_stack, fix=fix, memory=memory)
  [Previous line repeated 1 more time]
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/rules/base.py", line 162, in crawl
    raw_stack=raw_stack, memory=memory)
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/rules/std.py", line 488, in _eval
    segment, self.capitalisation_policy))
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/rules/std.py", line 463, in make_replacement
    return make_replacement(seg, list(cases_seen)[0])
  File "/Users/nolan/anaconda3/lib/python3.7/site-packages/sqlfluff/rules/std.py", line 469, in make_replacement
    raise ValueError("Unexpected capitalisation policy: {0!r}".format(policy))
ValueError: Unexpected capitalisation policy: 'inconsistent'
```


Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 654a7d54..2f59106b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+### Changed
+
+- Fixed the "inconsistent" bug in L010. Thanks [@nolanbconaway](https://github.com/nolanbconaway)
+
 ### Added
 
 - `--format` option to the `parse` command that allows a yaml output. This
diff --git a/src/sqlfluff/rules/std.py b/src/sqlfluff/rules/std.py
index 6d1b2c1f..71f0fff9 100644
--- a/src/sqlfluff/rules/std.py
+++ b/src/sqlfluff/rules/std.py
@@ -458,9 +458,15 @@ class Rule_L010(BaseCrawler):
                 elif policy == "capitalize":
                     new_raw = seg.raw.capitalize()
                 elif policy == "consistent":
-                    if cases_seen:
-                        # Get an element from what we've already seen
-                        return make_replacement(seg, list(cases_seen)[0])
+                    # The only case we DONT allow here is "inconsistent",
+                    # because it doesn't actually help us.
+                    filtered_cases_seen = [c for c in cases_seen if c != "inconsistent"]
+                    if filtered_cases_seen:
+                        # Get an element from what we've already seen.
+                        return make_replacement(
+                            seg,
+                            list(filtered_cases_seen)[0]
+                        )
                     else:
                         # If we haven't seen anything yet, then let's default
                         # to upper
@@ -477,7 +483,15 @@ class Rule_L010(BaseCrawler):
                 # No need to update memory
                 return LintResult(memory=memory)
             elif (
-                (self.capitalisation_policy == "consistent" and cases_seen and seen_case not in cases_seen)
+                # Are we required to be consistent? (and this is inconsistent?)
+                (
+                    self.capitalisation_policy == "consistent" and (
+                        # Either because we've seen multiple
+                        (cases_seen and seen_case not in cases_seen)
+                        # Or just because this one is inconsistent internally
+                        or seen_case == "inconsistent")
+                )
+                # Are we just required to be specfic?
                 # Policy is either upper, lower or capitalize
                 or (self.capitalisation_policy != "consistent" and seen_case != self.capitalisation_policy)
             ):


Test patch:
diff --git a/test/rules_std_test.py b/test/rules_std_test.py
index 90feb91d..146221b4 100644
--- a/test/rules_std_test.py
+++ b/test/rules_std_test.py
@@ -24,13 +24,22 @@ def assert_rule_fail_in_sql(code, sql):
     lerrs, _, _, _ = r.crawl(parsed, fix=True)
     print("Errors Found: {0}".format(lerrs))
     assert any([v.rule.code == code for v in lerrs])
-    fixes = []
-    for e in lerrs:
-        fixes += e.fixes
-    print("Fixes to apply: {0}".format(fixes))
+    fixed = parsed  # use this as our buffer (yes it's a bit of misnomer right here)
     while True:
-        l_fixes = fixes
-        fixed, fixes = parsed.apply_fixes(fixes)
+        # We get the errors again, but this time skip the assertion
+        # because we're in the loop. If we asserted on every loop then
+        # we're stuffed.
+        lerrs, _, _, _ = r.crawl(fixed, fix=True)
+        print("Errors Found: {0}".format(lerrs))
+        fixes = []
+        for e in lerrs:
+            fixes += e.fixes
+        if not fixes:
+            print("Done")
+            break
+        print("Fixes to apply: {0}".format(fixes))
+        l_fixes = fixes  # Save the fixes to compare to later
+        fixed, fixes = fixed.apply_fixes(fixes)
         # iterate until all fixes applied
         if fixes:
             if fixes == l_fixes:
@@ -38,8 +47,6 @@ def assert_rule_fail_in_sql(code, sql):
                     "Fixes aren't being applied: {0!r}".format(fixes))
             else:
                 continue
-        else:
-            break
     return fixed.raw
 
 
@@ -70,7 +77,10 @@ def assert_rule_pass_in_sql(code, sql):
     ('L014', 'fail', 'SELECT a,   B', 'SELECT a,   b'),
     ('L014', 'fail', 'SELECT B,   a', 'SELECT B,   A'),
     # Test that we don't fail * operators in brackets
-    ('L006', 'pass', 'SELECT COUNT(*) FROM tbl\n', None)
+    ('L006', 'pass', 'SELECT COUNT(*) FROM tbl\n', None),
+    # Test that we don't have the "inconsistent" bug
+    ('L010', 'fail', 'SeLeCt 1', 'SELECT 1'),
+    ('L010', 'fail', 'SeLeCt 1 from blah', 'SELECT 1 FROM blah'),
 ])
 def test__rules__std_string(rule, pass_fail, qry, fixed):
     """Test that a rule passes/fails on a given string.