support in db for constraints that reference the same column by appending a # followed by some unique string

This commit is contained in:
Lex Berezhny 2019-05-06 17:23:33 -04:00
parent ba8870823c
commit 31983fbb8e
2 changed files with 32 additions and 25 deletions

View file

@ -56,7 +56,11 @@ class TestQueryBuilder(unittest.TestCase):
def test_dot(self):
self.assertEqual(
constraints_to_sql({'txo.position': 18}),
('txo.position = :txo_position', {'txo_position': 18})
('txo.position = :txo_position0', {'txo_position0': 18})
)
self.assertEqual(
constraints_to_sql({'txo.position#6': 18}),
('txo.position = :txo_position6', {'txo_position6': 18})
)
def test_any(self):
@ -67,25 +71,25 @@ class TestQueryBuilder(unittest.TestCase):
'txo.age__lt': 38
}
}),
('(txo.age > :ages__any_txo_age__gt OR txo.age < :ages__any_txo_age__lt)', {
'ages__any_txo_age__gt': 18,
'ages__any_txo_age__lt': 38
('(txo.age > :ages__any0_txo_age__gt0 OR txo.age < :ages__any0_txo_age__lt0)', {
'ages__any0_txo_age__gt0': 18,
'ages__any0_txo_age__lt0': 38
})
)
def test_in(self):
self.assertEqual(
constraints_to_sql({'txo.age__in': [18, 38]}),
('txo.age IN (:txo_age__in0, :txo_age__in1)', {
'txo_age__in0': 18,
'txo_age__in1': 38
constraints_to_sql({'txo.age__in#2': [18, 38]}),
('txo.age IN (:txo_age__in2_0, :txo_age__in2_1)', {
'txo_age__in2_0': 18,
'txo_age__in2_1': 38
})
)
self.assertEqual(
constraints_to_sql({'txo.name__in': ('abc123', 'def456')}),
('txo.name IN (:txo_name__in0, :txo_name__in1)', {
'txo_name__in0': 'abc123',
'txo_name__in1': 'def456'
('txo.name IN (:txo_name__in0_0, :txo_name__in0_1)', {
'txo_name__in0_0': 'abc123',
'txo_name__in0_1': 'def456'
})
)
self.assertEqual(
@ -96,16 +100,16 @@ class TestQueryBuilder(unittest.TestCase):
def test_not_in(self):
self.assertEqual(
constraints_to_sql({'txo.age__not_in': [18, 38]}),
('txo.age NOT IN (:txo_age__not_in0, :txo_age__not_in1)', {
'txo_age__not_in0': 18,
'txo_age__not_in1': 38
('txo.age NOT IN (:txo_age__not_in0_0, :txo_age__not_in0_1)', {
'txo_age__not_in0_0': 18,
'txo_age__not_in0_1': 38
})
)
self.assertEqual(
constraints_to_sql({'txo.name__not_in': ('abc123', 'def456')}),
('txo.name NOT IN (:txo_name__not_in0, :txo_name__not_in1)', {
'txo_name__not_in0': 'abc123',
'txo_name__not_in1': 'def456'
('txo.name NOT IN (:txo_name__not_in0_0, :txo_name__not_in0_1)', {
'txo_name__not_in0_0': 'abc123',
'txo_name__not_in0_1': 'def456'
})
)
self.assertEqual(
@ -128,10 +132,10 @@ class TestQueryBuilder(unittest.TestCase):
a__not='b', b__in='select * from blah where c=:$c',
d__any={'one__like': 'o', 'two': 2}, limit=10, order_by='b', **{'$c': 3}),
(
"select * from foo WHERE a != :a__not AND "
"select * from foo WHERE a != :a__not0 AND "
"b IN (select * from blah where c=:$c) AND "
"(one LIKE :d__any_one__like OR two = :d__any_two) ORDER BY b LIMIT 10",
{'a__not': 'b', 'd__any_one__like': 'o', 'd__any_two': 2, '$c': 3}
"(one LIKE :d__any0_one__like0 OR two = :d__any0_two0) ORDER BY b LIMIT 10",
{'a__not0': 'b', 'd__any0_one__like0': 'o', 'd__any0_two0': 2, '$c': 3}
)
)

View file

@ -87,6 +87,9 @@ class AIOSQLite:
def constraints_to_sql(constraints, joiner=' AND ', prepend_key=''):
sql, values = [], {}
for key, constraint in constraints.items():
tag = '0'
if '#' in key:
key, tag = key[:key.index('#')], key[key.index('#')+1:]
col, op, key = key, '=', key.replace('.', '_')
if key.startswith('$'):
values[key] = constraint
@ -120,8 +123,8 @@ def constraints_to_sql(constraints, joiner=' AND ', prepend_key=''):
if isinstance(constraint, (list, set, tuple)):
keys = []
for i, val in enumerate(constraint):
keys.append(f':{key}{i}')
values[f'{key}{i}'] = val
keys.append(f':{key}{tag}_{i}')
values[f'{key}{tag}_{i}'] = val
sql.append(f'{col} {op} ({", ".join(keys)})')
elif isinstance(constraint, str):
sql.append(f'{col} {op} ({constraint})')
@ -129,12 +132,12 @@ def constraints_to_sql(constraints, joiner=' AND ', prepend_key=''):
raise ValueError(f"{col} requires a list, set or string as constraint value.")
continue
elif key.endswith('__any'):
where, subvalues = constraints_to_sql(constraint, ' OR ', key+'_')
where, subvalues = constraints_to_sql(constraint, ' OR ', key+tag+'_')
sql.append(f'({where})')
values.update(subvalues)
continue
sql.append(f'{col} {op} :{prepend_key}{key}')
values[prepend_key+key] = constraint
sql.append(f'{col} {op} :{prepend_key}{key}{tag}')
values[prepend_key+key+tag] = constraint
return joiner.join(sql) if sql else '', values