Coverage for dibbler / queries / query_helpers.py: 83%
42 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-01-25 14:26 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-01-25 14:26 +0000
1from datetime import datetime
2from typing import TypeVar
4from sqlalchemy import (
5 BindParameter,
6 ColumnExpressionArgument,
7 literal,
8 select,
9)
10from sqlalchemy.orm import QueryableAttribute
12from dibbler.models import Transaction
14T = TypeVar("T")
17def const(value: T) -> BindParameter[T]:
18 """
19 Create a constant SQL literal bind parameter.
21 This is useful to avoid too many `?` bind parameters in SQL queries,
22 when the input value is known to be safe.
23 """
25 return literal(value, literal_execute=True)
28CONST_ZERO: BindParameter[int] = const(0)
29"""A constant SQL expression `0`. This will render as a literal `0` in SQL queries."""
31CONST_ONE: BindParameter[int] = const(1)
32"""A constant SQL expression `1`. This will render as a literal `1` in SQL queries."""
34CONST_TRUE: BindParameter[bool] = const(True)
35"""A constant SQL expression `TRUE`. This will render as a literal `TRUE` in SQL queries."""
37CONST_FALSE: BindParameter[bool] = const(False)
38"""A constant SQL expression `FALSE`. This will render as a literal `FALSE` in SQL queries."""
40CONST_NONE: BindParameter[None] = const(None)
41"""A constant SQL expression `NULL`. This will render as a literal `NULL` in SQL queries."""
44def until_filter(
45 until_time: BindParameter[datetime] | None = None,
46 until_transaction_id: BindParameter[int] | None = None,
47 until_inclusive: bool = True,
48 transaction_time: QueryableAttribute = Transaction.time,
49) -> ColumnExpressionArgument[bool]:
50 """
51 Create a filter condition for transactions up to a given time or transaction.
53 Only one of `until_time` or `until_transaction_id` may be specified.
54 """
56 assert not (until_time is not None and until_transaction_id is not None), (
57 "Cannot filter by both until_time and until_transaction_id."
58 )
60 match (until_time, until_transaction_id, until_inclusive):
61 case (BindParameter(), None, True):
62 return transaction_time <= until_time
63 case (BindParameter(), None, False): 63 ↛ 64line 63 didn't jump to line 64 because the pattern on line 63 never matched
64 return transaction_time < until_time
65 case (None, BindParameter(), True):
66 return (
67 transaction_time
68 <= select(Transaction.time)
69 .where(Transaction.id == until_transaction_id)
70 .scalar_subquery()
71 )
72 case (None, BindParameter(), False): 72 ↛ 73line 72 didn't jump to line 73 because the pattern on line 72 never matched
73 return (
74 transaction_time
75 < select(Transaction.time)
76 .where(Transaction.id == until_transaction_id)
77 .scalar_subquery()
78 )
80 return CONST_TRUE
83def after_filter(
84 after_time: BindParameter[datetime] | None = None,
85 after_transaction_id: BindParameter[int] | None = None,
86 after_inclusive: bool = True,
87 transaction_time: QueryableAttribute = Transaction.time,
88) -> ColumnExpressionArgument[bool]:
89 """
90 Create a filter condition for transactions after a given time or transaction.
92 Only one of `after_time` or `after_transaction_id` may be specified.
93 """
95 assert not (after_time is not None and after_transaction_id is not None), (
96 "Cannot filter by both after_time and after_transaction_id."
97 )
99 match (after_time, after_transaction_id, after_inclusive):
100 case (BindParameter(), None, True): 100 ↛ 101line 100 didn't jump to line 101 because the pattern on line 100 never matched
101 return transaction_time >= after_time
102 case (BindParameter(), None, False): 102 ↛ 103line 102 didn't jump to line 103 because the pattern on line 102 never matched
103 return transaction_time > after_time
104 case (None, BindParameter(), True): 104 ↛ 105line 104 didn't jump to line 105 because the pattern on line 104 never matched
105 return (
106 transaction_time
107 >= select(Transaction.time)
108 .where(Transaction.id == after_transaction_id)
109 .scalar_subquery()
110 )
111 case (None, BindParameter(), False):
112 return (
113 transaction_time
114 > select(Transaction.time)
115 .where(Transaction.id == after_transaction_id)
116 .scalar_subquery()
117 )
119 return CONST_TRUE