Regression in 4ee6f55: get_conflicting_node fails to recognize conflict when filename contains non-ASCII uppercase character
============================================================================================================================
Since 4ee6f55, `get_conflicting_node` has been executing
```py
c.execute(CONFLICTING_NODE_SQL, [parent_id, name.lower()])
```
where
```py
CONFLICTING_NODE_SQL = """SELECT n.*, f.* FROM nodes n
JOIN parentage p ON n.id = p.child
LEFT OUTER JOIN files f ON n.id = f.id
WHERE p.parent = (?) AND LOWER(name) = (?) AND status = 'AVAILABLE'
ORDER BY n.name"""
```
Note the discrepancy in lowercase conversion here: SQLite's `lower` only lowers ASCII characters unless the ICU extension is loaded ([ref](https://www.sqlite.org/lang_corefunc.html#lower)) whereas Python's `str.lower` works on the full Unicode range.
For now I've worked around the issue by crippling lower on the Python side. Patch:
```diff
diff --git a/acdcli/cache/query.py b/acdcli/cache/query.py
index 469b953..a026866 100644
--- a/acdcli/cache/query.py
+++ b/acdcli/cache/query.py
@@ -1,4 +1,5 @@
import logging
+import string
from datetime import datetime
from .cursors import cursor
@@ -150,10 +151,20 @@ class QueryMixin(object):
def get_root_node(self):
return self.get_node(self.root_id)
+ @staticmethod
+ def asciilower(name: str):
+ '''Returns a copy of name with all ASCII characters converted to lower case.
+
+ For compatibility with SQLite lower(X) (with ICU extension).
+ See https://www.sqlite.org/lang_corefunc.html#lower.
+ '''
+ table = str.maketrans(string.ascii_uppercase, string.ascii_lowercase)
+ return name.translate(table)
+
def get_conflicting_node(self, name: str, parent_id: str):
"""Finds conflicting node in folder specified by *parent_id*, if one exists."""
with cursor(self._conn) as c:
- c.execute(CONFLICTING_NODE_SQL, [parent_id, name.lower()])
+ c.execute(CONFLICTING_NODE_SQL, [parent_id, self.asciilower(name)])
r = c.fetchone()
if r:
return Node(r)
```
This is not ideal, though, because according to testing the case insensitivity on the ACD side applies to more than just ASCII; for instance, I couldn't create both `Ⅱ` and `ⅱ`.