ransacklib 1.1.0.dev13__tar.gz → 1.1.0.dev15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {ransacklib-1.1.0.dev13/ransacklib.egg-info → ransacklib-1.1.0.dev15}/PKG-INFO +1 -1
  2. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/pyproject.toml +1 -1
  3. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransack/function.py +4 -1
  4. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransack/operator.py +1 -0
  5. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransack/transformer.py +2 -7
  6. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15/ransacklib.egg-info}/PKG-INFO +1 -1
  7. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/tests/test_postgres.py +125 -3
  8. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/tests/test_transformer.py +1 -123
  9. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/LICENSE +0 -0
  10. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/README.rst +0 -0
  11. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransack/__init__.py +0 -0
  12. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransack/exceptions.py +0 -0
  13. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransack/parser.py +0 -0
  14. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransack/py.typed +0 -0
  15. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransack/types.py +0 -0
  16. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransacklib.egg-info/SOURCES.txt +0 -0
  17. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransacklib.egg-info/dependency_links.txt +0 -0
  18. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransacklib.egg-info/requires.txt +0 -0
  19. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/ransacklib.egg-info/top_level.txt +0 -0
  20. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/setup.cfg +0 -0
  21. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/tests/test_operator.py +0 -0
  22. {ransacklib-1.1.0.dev13 → ransacklib-1.1.0.dev15}/tests/test_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ransacklib
3
- Version: 1.1.0.dev13
3
+ Version: 1.1.0.dev15
4
4
  Summary: A modern, extensible language for manipulation with structured data
5
5
  Author-email: "Rajmund H. Hruška" <rajmund.hruska@cesnet.cz>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ransacklib"
7
- version = "1.1.0.dev13"
7
+ version = "1.1.0.dev15"
8
8
  description = "A modern, extensible language for manipulation with structured data"
9
9
  license = "MIT"
10
10
  readme = "README.rst"
@@ -17,12 +17,15 @@ Example usage in query:
17
17
  """
18
18
 
19
19
  import datetime
20
+ from collections.abc import MutableSequence
20
21
 
21
22
  from ipranges import IP4, IP6, IP4Net, IP4Range, IP6Net, IP6Range
22
23
 
23
24
 
24
25
  def length(x):
25
- if isinstance(x, (list, str, IP4, IP4Net, IP4Range, IP6, IP6Net, IP6Range)):
26
+ if isinstance(
27
+ x, (MutableSequence, str, IP4, IP4Net, IP4Range, IP6, IP6Net, IP6Range)
28
+ ):
26
29
  return len(x)
27
30
  raise TypeError(f"Function length is not defined for value {x}")
28
31
 
@@ -635,6 +635,7 @@ def _operator_map_sql(op, l_sql, r_sql, t1, t2) -> tuple[str, typing.Any, bool]:
635
635
  (String, List(String)): (f"{l_sql} = ANY({r_sql})", False),
636
636
  (IP, IP): (f"{l_sql} && {r_sql}", False),
637
637
  (IP, List(IP)): (f"{l_sql} && ANY({r_sql})", False),
638
+ (List(IP), IP): (f"{r_sql} && ANY({l_sql})", True),
638
639
  (List(Any), List(Any)): (f"{l_sql} && {r_sql}", False),
639
640
  (Range(Any), Range(Any)): (f"{l_sql} && {r_sql}", False),
640
641
  },
@@ -871,12 +871,7 @@ class SQLInterpreter(Interpreter):
871
871
  return ("%s", [token.real_value], types.String)
872
872
 
873
873
  def ip(self, token: TokenWrapper) -> tuple[str, list, types.RansackType]:
874
- match token.real_value:
875
- case IP4() | IP6():
876
- cast = "ipaddress"
877
- case IP4Range() | IP6Range() | IP4Net() | IP6Net():
878
- cast = "iprange"
879
- return (f"%s::{cast}", [token.real_value], types.IP)
874
+ return ("%s::iprange", [token.real_value], types.IP)
880
875
 
881
876
  def neg(self, tree: Tree) -> tuple[str, list, Any]:
882
877
  sql, params, type_ = self.visit(tree)
@@ -903,7 +898,7 @@ class SQLInterpreter(Interpreter):
903
898
  range_type = "tsrange"
904
899
  elif l_type is types.IP and r_type is types.IP:
905
900
  return (
906
- f"iprange({l_sql}, {r_sql})",
901
+ f"iprange({l_sql}::text::ipaddress, {r_sql}::text::ipaddress)",
907
902
  l_params + r_params,
908
903
  types.IP,
909
904
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ransacklib
3
- Version: 1.1.0.dev13
3
+ Version: 1.1.0.dev15
4
4
  Summary: A modern, extensible language for manipulation with structured data
5
5
  Author-email: "Rajmund H. Hruška" <rajmund.hruska@cesnet.cz>
6
6
  License-Expression: MIT
@@ -2,9 +2,10 @@ from datetime import datetime, timedelta, timezone
2
2
  from zoneinfo import ZoneInfo
3
3
 
4
4
  import pytest
5
+ from ipranges import IP4, IP6, IP4Net, IP4Range, IP6Net, IP6Range
5
6
  from psycopg.types.range import Range
6
7
 
7
- from ransack import Parser, SQLInterpreter, types
8
+ from ransack import Parser, RansackError, SQLInterpreter, types
8
9
 
9
10
 
10
11
  @pytest.fixture
@@ -27,9 +28,11 @@ def test_data(db):
27
28
  ts_value timestamp,
28
29
  tstz_value timestamptz,
29
30
  interval_value interval,
31
+ ip_value iprange,
30
32
 
31
33
  int_array integer[],
32
34
  text_array text[],
35
+ iprange_array iprange[],
33
36
  interval_array interval[],
34
37
  null_array integer[]
35
38
  );
@@ -39,8 +42,8 @@ def test_data(db):
39
42
  f"""
40
43
  INSERT INTO {table}
41
44
  VALUES (
42
- %s, %s, %s, %s, %s, %s, %s, %s, %s,
43
- %s, %s, %s, %s
45
+ %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
46
+ %s, %s, %s, %s, %s
44
47
  )
45
48
  """,
46
49
  (
@@ -53,8 +56,10 @@ def test_data(db):
53
56
  datetime(2025, 1, 1, 12, 0, 0),
54
57
  datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
55
58
  timedelta(days=2, seconds=123),
59
+ IP4("192.168.1.120"),
56
60
  [1, 2, 3],
57
61
  ["a", "b", "c"],
62
+ [IP4("192.168.0.1"), IP4("192.168.0.20"), IP4Net("192.168.1.0/24")],
58
63
  [
59
64
  timedelta(hours=1),
60
65
  timedelta(hours=2),
@@ -83,8 +88,10 @@ def ransack_sql_mapping():
83
88
  "creation_time": ("ts_value", types.Datetime),
84
89
  "detection_time": ("tstz_value", types.Datetime),
85
90
  "delta": ("interval_value", types.Timedelta),
91
+ "source_ip": ("ip_value", types.IP),
86
92
  "ports": ("int_array", types.List[types.Number]),
87
93
  "categories": ("text_array", types.List[types.String]),
94
+ "iprange_array": ("iprange_array", types.List(types.IP)),
88
95
  "deltas": ("interval_array", types.List[types.Timedelta]),
89
96
  "null_array": ("null_array", types.List[types.Number]),
90
97
  }
@@ -135,6 +142,13 @@ def test_connection(db):
135
142
  ("''", ""),
136
143
  ('"string"', "string"),
137
144
  ('"\';DROP TABLE USERS"', "';DROP TABLE USERS"),
145
+ # IP.
146
+ ("192.168.0.0", IP4("192.168.0.0")),
147
+ ("192.168.0.0/16", IP4Net("192.168.0.0/16")),
148
+ ("192.168.0.10-192.168.0.50", IP4Range("192.168.0.10-192.168.0.50")),
149
+ ("fd00::1", IP6("fd00::1")),
150
+ ("fd00::/8", IP6Net("fd00::/8")),
151
+ ("fd00::1-fd00::10", IP6Range("fd00::1-fd00::10")),
138
152
  ],
139
153
  )
140
154
  def test_basic_types(db, expr, expected):
@@ -152,6 +166,19 @@ def test_basic_types(db, expr, expected):
152
166
  ("[1, 2, 3]", [1, 2, 3]),
153
167
  ("[01:23:34]", [timedelta(seconds=5014)]),
154
168
  ('[["a"]]', [["a"]]),
169
+ ("[192.168.0.0, 192.168.0.1]", [IP4("192.168.0.0"), IP4("192.168.0.1")]),
170
+ (
171
+ "[192.168.0.0, 192.168.0.0/16, 192.168.0.10-192.168.0.50]",
172
+ [
173
+ IP4("192.168.0.0"),
174
+ IP4Net("192.168.0.0/16"),
175
+ IP4Range("192.168.0.10-192.168.0.50"),
176
+ ],
177
+ ),
178
+ (
179
+ "[fd00::1, fd00::/8, fd00::1-fd00::10]",
180
+ [IP6("fd00::1"), IP6Net("fd00::/8"), IP6Range("fd00::1-fd00::10")],
181
+ ),
155
182
  # Ranges.
156
183
  ("100..1", Range(1, 101, "[)")),
157
184
  ("100.12..1.14", Range(1.14, 100.12, "[]")),
@@ -175,6 +202,8 @@ def test_basic_types(db, expr, expected):
175
202
  "[]",
176
203
  ),
177
204
  ),
205
+ ("192.168.0.10..192.168.0.50", IP4Range("192.168.0.10-192.168.0.50")),
206
+ ("fd00::1..fd00::10", IP6Range("fd00::1-fd00::10")),
178
207
  ],
179
208
  )
180
209
  def test_container_types(db, expr, expected):
@@ -195,8 +224,13 @@ def test_container_types(db, expr, expected):
195
224
  ("creation_time", datetime(2025, 1, 1, 12, 0)),
196
225
  ("detection_time", datetime(2025, 1, 1, 12, 0, tzinfo=ZoneInfo(key="Etc/UTC"))),
197
226
  ("delta", timedelta(days=2, seconds=123)),
227
+ ("source_ip", IP4("192.168.1.120")),
198
228
  ("ports", [1, 2, 3]),
199
229
  ("categories", ["a", "b", "c"]),
230
+ (
231
+ "iprange_array",
232
+ [IP4("192.168.0.1"), IP4("192.168.0.20"), IP4Net("192.168.1.0/24")],
233
+ ),
200
234
  ("deltas", [timedelta(seconds=3600), timedelta(seconds=7200)]),
201
235
  ("[a_int, b_int]", [12, -200]),
202
236
  ("a_int..b_int", Range(-200, 13, "[)")),
@@ -303,6 +337,7 @@ def test_concat_operator(db, expr, expected, test_data, ransack_sql_mapping):
303
337
  ("len(ports)", 3),
304
338
  ("len(categories)", 3),
305
339
  ("length([0, 1, 2, 3])", 4),
340
+ ("length(iprange_array)", 3),
306
341
  ],
307
342
  )
308
343
  def test_functions(db, expr, expected, test_data, ransack_sql_mapping):
@@ -897,6 +932,24 @@ def test_any_equal_operator(db, expr, expected):
897
932
  ("delta = [01:00:00]", False),
898
933
  ("01:00:00 = deltas", True),
899
934
  ("03:00:00 = deltas", False),
935
+ # IP = IP
936
+ ("10.10.10.10 = 10.10.10.10", True),
937
+ ("10.10.10.10 = 10.10.10.0 .. 10.10.10.20", True),
938
+ ("192.168.0.5 = 192.168.0.0-192.168.0.255", True),
939
+ ("192.168.0.12 = 192.168.1.0/24", False),
940
+ ("10.10.10.10-10.10.10.20 = 10.10.10.15-10.10.10.25", True),
941
+ ("10.10.10.10-10.10.10.20 = 192.168.0.0/16", False),
942
+ ("192.168.1.0/24 = 192.168.0.0/16", True),
943
+ # IP = List[IP]
944
+ ("192.168.1.10 = [192.168.0.0/24, 192.168.0.0/16]", True),
945
+ ("192.168.1.10 = [10.0.0.0/8, 172.16.0.0/12]", False),
946
+ ("2001:db8::1 = [2001:db8:1::/48, 2001:db8::/32]", True),
947
+ ("2001:db8::1 = [fd00::/8, fe80::/10]", False),
948
+ # List[IP] = IP
949
+ ("[192.168.0.0/24, 192.168.0.0/16] = 192.168.1.10", True),
950
+ ("[10.0.0.0/8, 172.16.0.0/12] = 192.168.1.10", False),
951
+ ("[2001:db8:1::/48, 2001:db8::/32] = 2001:db8::1", True),
952
+ ("[fd00::/8, fe80::/10] = 2001:db8::1", False),
900
953
  # List = List (overlap).
901
954
  ("ports = [3, 4, 5]", True),
902
955
  ("ports = [4, 5]", False),
@@ -992,6 +1045,57 @@ def test_any_equal_operator_variables(
992
1045
  ("01:00:00 >= 01:00:00", True),
993
1046
  ("01:00:00 < 02:00:00", True),
994
1047
  ("01:00:00 <= 01:00:00", True),
1048
+ # IP x IP.
1049
+ ("10.0.0.2 > 10.0.0.1", True),
1050
+ ("10.0.0.1 > 10.0.0.2", False),
1051
+ ("10.0.0.1 >= 10.0.0.1", True),
1052
+ ("10.0.0.1 >= 10.0.0.2", False),
1053
+ ("10.0.0.1 < 10.0.0.2", True),
1054
+ ("10.0.0.2 < 10.0.0.1", False),
1055
+ ("10.0.0.1 <= 10.0.0.1", True),
1056
+ ("10.0.0.2 <= 10.0.0.1", False),
1057
+ ("2001:db8::2 > 2001:db8::1", True),
1058
+ ("2001:db8::1 > 2001:db8::2", False),
1059
+ ("2001:db8::1 >= 2001:db8::1", True),
1060
+ ("2001:db8::1 >= 2001:db8::2", False),
1061
+ ("2001:db8::1 < 2001:db8::2", True),
1062
+ ("2001:db8::2 < 2001:db8::1", False),
1063
+ ("2001:db8::1 <= 2001:db8::1", True),
1064
+ ("2001:db8::2 <= 2001:db8::1", False),
1065
+ # IP x List(IP).
1066
+ ("10.0.0.5 > [10.0.0.1, 10.0.0.10]", True),
1067
+ ("10.0.0.0 > [10.0.0.1, 10.0.0.10]", False),
1068
+ ("10.0.0.5 >= [10.0.0.5, 10.0.0.10]", True),
1069
+ ("10.0.0.4 >= [10.0.0.5, 10.0.0.10]", False),
1070
+ ("10.0.0.5 < [10.0.0.1, 10.0.0.10]", True),
1071
+ ("10.0.0.20 < [10.0.0.1, 10.0.0.10]", False),
1072
+ ("10.0.0.5 <= [10.0.0.1, 10.0.0.5]", True),
1073
+ ("10.0.0.20 <= [10.0.0.1, 10.0.0.5]", False),
1074
+ ("2001:db8::5 > [2001:db8::1, 2001:db8::10]", True),
1075
+ ("2001:db8::0 > [2001:db8::1, 2001:db8::10]", False),
1076
+ ("2001:db8::5 >= [2001:db8::5, 2001:db8::10]", True),
1077
+ ("2001:db8::4 >= [2001:db8::5, 2001:db8::10]", False),
1078
+ ("2001:db8::5 < [2001:db8::1, 2001:db8::10]", True),
1079
+ ("2001:db8::20 < [2001:db8::1, 2001:db8::10]", False),
1080
+ ("2001:db8::5 <= [2001:db8::1, 2001:db8::5]", True),
1081
+ ("2001:db8::20 <= [2001:db8::1, 2001:db8::5]", False),
1082
+ # List(IP) x IP.
1083
+ ("[10.0.0.1, 10.0.0.10] > 10.0.0.5", True),
1084
+ ("[10.0.0.1, 10.0.0.2] > 10.0.0.5", False),
1085
+ ("[10.0.0.5, 10.0.0.10] >= 10.0.0.5", True),
1086
+ ("[10.0.0.1, 10.0.0.2] >= 10.0.0.5", False),
1087
+ ("[10.0.0.1, 10.0.0.10] < 10.0.0.5", True),
1088
+ ("[10.0.0.10, 10.0.0.20] < 10.0.0.5", False),
1089
+ ("[10.0.0.1, 10.0.0.5] <= 10.0.0.5", True),
1090
+ ("[10.0.0.10, 10.0.0.20] <= 10.0.0.5", False),
1091
+ ("[2001:db8::1, 2001:db8::10] > 2001:db8::5", True),
1092
+ ("[2001:db8::1, 2001:db8::2] > 2001:db8::5", False),
1093
+ ("[2001:db8::5, 2001:db8::10] >= 2001:db8::5", True),
1094
+ ("[2001:db8::1, 2001:db8::2] >= 2001:db8::5", False),
1095
+ ("[2001:db8::1, 2001:db8::10] < 2001:db8::5", True),
1096
+ ("[2001:db8::10, 2001:db8::20] < 2001:db8::5", False),
1097
+ ("[2001:db8::1, 2001:db8::5] <= 2001:db8::5", True),
1098
+ ("[2001:db8::10, 2001:db8::20] <= 2001:db8::5", False),
995
1099
  # Empty arrays.
996
1100
  ("1 > []", False),
997
1101
  ("1 >= []", False),
@@ -1176,3 +1280,21 @@ def test_complex_expressions(
1176
1280
  params,
1177
1281
  )
1178
1282
  assert cur.fetchone()[0] is expected
1283
+
1284
+
1285
+ @pytest.mark.sql
1286
+ @pytest.mark.parametrize(
1287
+ ("expr"),
1288
+ [
1289
+ "2000-12-24..192.168.0.50", # Unsupported type of range.
1290
+ "1..1.5",
1291
+ "1..2025-01-01",
1292
+ '"abc"..123',
1293
+ "01:00:00..5",
1294
+ "not_there", # Unknown column.
1295
+ ],
1296
+ )
1297
+ def test_invalid_cases(expr):
1298
+ tree = Parser().parse(expr)
1299
+ with pytest.raises(RansackError):
1300
+ SQLInterpreter().to_sql(tree)
@@ -1,7 +1,7 @@
1
1
  from datetime import datetime, timedelta, timezone
2
2
 
3
3
  import pytest
4
- from ipranges import IP4, IP6, IP4Net, IP4Range, IP6Net, IP6Range, from_str
4
+ from ipranges import IP4, IP6, IP4Net, IP4Range, IP6Net, IP6Range
5
5
  from lark import Token
6
6
 
7
7
  from ransack.exceptions import RansackError, ShapeError
@@ -13,7 +13,6 @@ from ransack.transformer import (
13
13
  TokenWrapper,
14
14
  get_values,
15
15
  )
16
- from ransack.types import Integer, List, String
17
16
 
18
17
 
19
18
  @pytest.fixture
@@ -574,124 +573,3 @@ class TestFilter:
574
573
  exp_tree = expr_transformer.transform(parser.parse_only("my_var == 14"))
575
574
  res = filter_.transform(exp_tree)
576
575
  assert res
577
-
578
-
579
- class TestSQLInterpreter:
580
- def parse(self, parser, sql, expression):
581
- tree = parser.parse(expression)
582
- data = {
583
- "a_int": ("a", Integer),
584
- "b_int": ("b", Integer),
585
- "c_int": ("c", Integer),
586
- "d_int": ("d", Integer),
587
- "Description": ("description", String),
588
- "Category": ("category", List[String]),
589
- }
590
- return sql.to_sql(tree, data)
591
-
592
- @pytest.mark.parametrize(
593
- ("expr", "expected_sql", "expected_params"),
594
- [
595
- # IPs.
596
- ("192.168.0.0", "%s::ipaddress", [from_str("192.168.0.0")]),
597
- ("192.168.0.0/16", "%s::iprange", [from_str("192.168.0.0/16")]),
598
- (
599
- "192.168.0.10-192.168.0.50",
600
- "%s::iprange",
601
- [from_str("192.168.0.10-192.168.0.50")],
602
- ),
603
- ("fd00::1", "%s::ipaddress", [from_str("fd00::1")]),
604
- ("fd00::/8", "%s::iprange", [from_str("fd00::/8")]),
605
- ("fd00::1-fd00::10", "%s::iprange", [from_str("fd00::1-fd00::10")]),
606
- (
607
- "192.168.0.10..192.168.0.50",
608
- "iprange(%s::ipaddress, %s::ipaddress)",
609
- [from_str("192.168.0.10"), from_str("192.168.0.50")],
610
- ),
611
- (
612
- "fd00::1..fd00::10",
613
- "iprange(%s::ipaddress, %s::ipaddress)",
614
- [from_str("fd00::1"), from_str("fd00::10")],
615
- ),
616
- # Test = operator.
617
- (
618
- "10.10.10.10 = 10.10.10.10",
619
- "%s::ipaddress && %s::ipaddress",
620
- [from_str("10.10.10.10"), from_str("10.10.10.10")],
621
- ),
622
- (
623
- "10.10.10.10 = 10.10.10.0 .. 10.10.10.20",
624
- "%s::ipaddress && iprange(%s::ipaddress, %s::ipaddress)",
625
- [
626
- from_str("10.10.10.10"),
627
- from_str("10.10.10.0"),
628
- from_str("10.10.10.20"),
629
- ],
630
- ),
631
- (
632
- "192.168.0.5 = 192.168.0.0-192.168.0.255",
633
- "%s::ipaddress && %s::iprange",
634
- [from_str("192.168.0.5"), from_str("192.168.0.0-192.168.0.255")],
635
- ),
636
- (
637
- "192.168.0.12 = 192.168.1.0/24",
638
- "%s::ipaddress && %s::iprange",
639
- [from_str("192.168.0.12"), from_str("192.168.1.0/24")],
640
- ),
641
- (
642
- "10.10.10.10-10.10.10.20 = 10.10.10.15-10.10.10.25",
643
- "%s::iprange && %s::iprange",
644
- [
645
- from_str("10.10.10.10-10.10.10.20"),
646
- from_str("10.10.10.15-10.10.10.25"),
647
- ],
648
- ),
649
- (
650
- "10.10.10.10-10.10.10.20 = 192.168.0.0/16",
651
- "%s::iprange && %s::iprange",
652
- [from_str("10.10.10.10-10.10.10.20"), from_str("192.168.0.0/16")],
653
- ),
654
- (
655
- "192.168.1.0/24 = 192.168.0.0/16",
656
- "%s::iprange && %s::iprange",
657
- [from_str("192.168.1.0/24"), from_str("192.168.0.0/16")],
658
- ),
659
- (
660
- "192.168.1.10 = [192.168.0.0/24, 192.168.0.0/16]",
661
- ("%s::ipaddress && ANY(ARRAY[%s::iprange, %s::iprange])"),
662
- [
663
- from_str("192.168.1.10"),
664
- from_str("192.168.0.0/24"),
665
- from_str("192.168.0.0/16"),
666
- ],
667
- ),
668
- ],
669
- )
670
- def test_parse_sql(self, parser, sql, expr, expected_sql, expected_params):
671
- sql_str, params, _ = self.parse(parser, sql, expr)
672
-
673
- assert sql_str == expected_sql
674
- assert params == expected_params
675
-
676
- def to_sql_and_expect_error(self, parser, sql, expression):
677
- tree = parser.parse(expression)
678
- with pytest.raises(RansackError):
679
- sql.to_sql(tree)
680
-
681
- @pytest.mark.parametrize(
682
- "expression",
683
- [
684
- "2000-12-24..192.168.0.50", # Unsupported type of range.
685
- "1..1.5",
686
- "1..2025-01-01",
687
- '"abc"..123',
688
- "01:00:00..5",
689
- "not_there", # Unknown column.
690
- ],
691
- )
692
- def test_invalid_cases(self, parser, sql, expression):
693
- self.to_sql_and_expect_error(
694
- parser,
695
- sql,
696
- expression,
697
- )