ransacklib 1.1.0.dev5__tar.gz → 1.1.0.dev6__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 (20) hide show
  1. {ransacklib-1.1.0.dev5/ransacklib.egg-info → ransacklib-1.1.0.dev6}/PKG-INFO +1 -1
  2. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/pyproject.toml +1 -1
  3. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransack/operator.py +55 -47
  4. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransack/transformer.py +28 -27
  5. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6/ransacklib.egg-info}/PKG-INFO +1 -1
  6. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/tests/test_transformer.py +106 -3
  7. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/LICENSE +0 -0
  8. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/README.rst +0 -0
  9. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransack/__init__.py +0 -0
  10. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransack/exceptions.py +0 -0
  11. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransack/function.py +0 -0
  12. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransack/parser.py +0 -0
  13. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransack/py.typed +0 -0
  14. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransacklib.egg-info/SOURCES.txt +0 -0
  15. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransacklib.egg-info/dependency_links.txt +0 -0
  16. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransacklib.egg-info/requires.txt +0 -0
  17. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/ransacklib.egg-info/top_level.txt +0 -0
  18. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/setup.cfg +0 -0
  19. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/tests/test_operator.py +0 -0
  20. {ransacklib-1.1.0.dev5 → ransacklib-1.1.0.dev6}/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.dev5
3
+ Version: 1.1.0.dev6
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.dev5"
7
+ version = "1.1.0.dev6"
8
8
  description = "A modern, extensible language for manipulation with structured data"
9
9
  license = "MIT"
10
10
  readme = "README.rst"
@@ -500,63 +500,65 @@ def binary_operation(operator: str, left: Any, right: Any) -> Any:
500
500
  raise OperatorNotFoundError(operator, (t1, t2), (left, right)) from None
501
501
 
502
502
 
503
- def _operator_map_sql(op, l_sql, r_sql, t1, t2) -> tuple[str, Any]:
504
- def _get_comp_dict_sql(op: str, tuple_repr_f) -> dict[tuple[Operand, Operand], str]:
503
+ def _operator_map_sql(op, l_sql, r_sql, t1, t2) -> tuple[str, Any, bool]:
504
+ def _get_comp_dict_sql(
505
+ op: str, tuple_repr_f
506
+ ) -> dict[tuple[Operand, Operand], tuple[str, bool]]:
505
507
  return {
506
- (Number, Number): f"{l_sql} {op} {r_sql}",
507
- (Number, list): f"{l_sql} {op} ANY({r_sql})",
508
- (Number, tuple): f"{l_sql} {op} {tuple_repr_f}({r_sql})",
509
- (datetime, datetime): f"{l_sql} {op} {r_sql}",
510
- (datetime, list): f"{l_sql} {op} ANY({r_sql})",
511
- (datetime, tuple): f"{r_sql} {op} {tuple_repr_f}({l_sql})",
512
- (timedelta, timedelta): f"{l_sql} {op} {r_sql}",
513
- (timedelta, list): f"{l_sql} {op} ANY({r_sql})",
514
- ("ip", "ip"): f"{l_sql} {op} {r_sql}",
515
- ("ip", list): f"{l_sql} {op} ANY({r_sql})",
508
+ (Number, Number): (f"{l_sql} {op} {r_sql}", False),
509
+ (Number, list): (f"{l_sql} {op} ANY({r_sql})", False),
510
+ (Number, tuple): (f"{l_sql} {op} {tuple_repr_f}({r_sql})", False),
511
+ (datetime, datetime): (f"{l_sql} {op} {r_sql}", False),
512
+ (datetime, list): (f"{l_sql} {op} ANY({r_sql})", False),
513
+ (datetime, tuple): (f"{r_sql} {op} {tuple_repr_f}({l_sql})", True),
514
+ (timedelta, timedelta): (f"{l_sql} {op} {r_sql}", False),
515
+ (timedelta, list): (f"{l_sql} {op} ANY({r_sql})", False),
516
+ ("ip", "ip"): (f"{l_sql} {op} {r_sql}", False),
517
+ ("ip", list): (f"{l_sql} {op} ANY({r_sql})", False),
516
518
  # (list, list): f"{l_sql} && {r_sql}",
517
519
  # (tuple, tuple): f"upper({l_sql}) > lower({r_sql})",
518
520
  }
519
521
 
520
522
  d: dict[str, dict] = {
521
523
  "=": {
522
- (Number, Number): f"{l_sql} = {r_sql}",
523
- (Number, list): f"{l_sql} = ANY({r_sql})",
524
- (Number, tuple): f"{r_sql} @> {l_sql}",
525
- (datetime, datetime): f"{l_sql} = {r_sql}",
526
- (datetime, list): f"{l_sql} = ANY({r_sql})",
527
- (datetime, tuple): f"{r_sql} @> {l_sql}",
528
- (timedelta, timedelta): f"{l_sql} = {r_sql}",
529
- (timedelta, list): f"{l_sql} = ANY({r_sql})",
530
- (str, str): f"{l_sql} = {r_sql}",
531
- (str, list): f"{l_sql} = ANY({r_sql})",
532
- ("ip", "ip"): f"{l_sql} && {r_sql}",
533
- ("ip", list): f"{l_sql} && ANY({r_sql})",
534
- (list, list): f"{l_sql} && {r_sql}",
535
- (tuple, tuple): f"{l_sql} && {r_sql}",
524
+ (Number, Number): (f"{l_sql} = {r_sql}", False),
525
+ (Number, list): (f"{l_sql} = ANY({r_sql})", False),
526
+ (Number, tuple): (f"{r_sql} @> {l_sql}", True),
527
+ (datetime, datetime): (f"{l_sql} = {r_sql}", False),
528
+ (datetime, list): (f"{l_sql} = ANY({r_sql})", False),
529
+ (datetime, tuple): (f"{r_sql} @> {l_sql}", True),
530
+ (timedelta, timedelta): (f"{l_sql} = {r_sql}", False),
531
+ (timedelta, list): (f"{l_sql} = ANY({r_sql})", False),
532
+ (str, str): (f"{l_sql} = {r_sql}", False),
533
+ (str, list): (f"{l_sql} = ANY({r_sql})", False),
534
+ ("ip", "ip"): (f"{l_sql} && {r_sql}", False),
535
+ ("ip", list): (f"{l_sql} && ANY({r_sql})", False),
536
+ (list, list): (f"{l_sql} && {r_sql}", False),
537
+ (tuple, tuple): (f"{l_sql} && {r_sql}", False),
536
538
  },
537
539
  ">": _get_comp_dict_sql(">", "lower")
538
- | {(tuple, tuple): f"upper({l_sql}) > lower({r_sql})"},
540
+ | {(tuple, tuple): (f"upper({l_sql}) > lower({r_sql})", False)},
539
541
  ">=": _get_comp_dict_sql(">=", "lower")
540
- | {(tuple, tuple): f"upper({l_sql}) >= lower({r_sql})"},
542
+ | {(tuple, tuple): (f"upper({l_sql}) >= lower({r_sql})", False)},
541
543
  "<": _get_comp_dict_sql("<", "upper")
542
- | {(tuple, tuple): f"lower({l_sql}) < upper({r_sql})"},
544
+ | {(tuple, tuple): (f"lower({l_sql}) < upper({r_sql})", False)},
543
545
  "<=": _get_comp_dict_sql("<=", "upper")
544
- | {(tuple, tuple): f"lower({l_sql}) <= upper({r_sql})"},
545
- "and": {(bool, bool): f"{l_sql} AND {r_sql}"},
546
- "or": {(bool, bool): f"{l_sql} OR {r_sql}"},
547
- "contains": {(str, str): f"position({r_sql} in {l_sql})>0"},
546
+ | {(tuple, tuple): (f"lower({l_sql}) <= upper({r_sql})", False)},
547
+ "and": {(bool, bool): (f"{l_sql} AND {r_sql}", False)},
548
+ "or": {(bool, bool): (f"({l_sql} OR {r_sql})", False)},
549
+ "contains": {(str, str): (f"position({r_sql} in {l_sql})>0", True)},
548
550
  }
549
551
  d2: dict[str, dict] = {
550
552
  "+": {
551
- (Number, Number): (f"{l_sql} + {r_sql}", Number),
552
- (datetime, timedelta): (f"{l_sql} + {r_sql}", datetime),
553
- (timedelta, timedelta): (f"{l_sql} + {r_sql}", timedelta),
553
+ (Number, Number): (f"({l_sql} + {r_sql})", Number),
554
+ (datetime, timedelta): (f"({l_sql} + {r_sql})", datetime),
555
+ (timedelta, timedelta): (f"({l_sql} + {r_sql})", timedelta),
554
556
  },
555
557
  "-": {
556
- (Number, Number): (f"{l_sql} - {r_sql}", Number),
557
- (datetime, timedelta): (f"{l_sql} - {r_sql}", datetime),
558
- (timedelta, timedelta): (f"{l_sql} - {r_sql}", timedelta),
559
- (datetime, datetime): (f"{l_sql} - {r_sql}", timedelta),
558
+ (Number, Number): (f"({l_sql} - {r_sql})", Number),
559
+ (datetime, timedelta): (f"({l_sql} - {r_sql})", datetime),
560
+ (timedelta, timedelta): (f"({l_sql} - {r_sql})", timedelta),
561
+ (datetime, datetime): (f"({l_sql} - {r_sql})", timedelta),
560
562
  },
561
563
  "*": {
562
564
  (timedelta, Number): (f"{l_sql} * {r_sql}", timedelta),
@@ -567,21 +569,24 @@ def _operator_map_sql(op, l_sql, r_sql, t1, t2) -> tuple[str, Any]:
567
569
  (Number, Number): (f"{l_sql} / {r_sql}", Number),
568
570
  },
569
571
  "%": {
570
- (Number, Number): (f"{l_sql} % {r_sql}", Number),
572
+ (Number, Number): (f"({l_sql} % {r_sql})", Number),
571
573
  },
572
574
  }
573
575
  if op == "==":
574
- return (f"{l_sql} = {r_sql}", bool)
576
+ return (f"{l_sql} = {r_sql}", bool, False)
575
577
  if op == "in":
576
- return (d["="][(t1, t2)], bool)
578
+ sql, switch = d["="][(t1, t2)]
579
+ return sql, bool, switch
577
580
  if op in d2:
578
- return d2[op][(t1, t2)]
579
- return (d[op][(t1, t2)], bool)
581
+ sql, type_ = d2[op][(t1, t2)]
582
+ return sql, type_, False
583
+ sql, switch = d[op][(t1, t2)]
584
+ return sql, bool, switch
580
585
 
581
586
 
582
587
  def binary_operation_sql(
583
588
  operator: str, left: str, right: str, l_type: Any, r_type: Any
584
- ) -> tuple[str, Any]:
589
+ ) -> tuple[str, bool, Any]:
585
590
  # Normalize numeric types
586
591
  l_type = Number if l_type in (int, float) else l_type
587
592
  r_type = Number if r_type in (int, float) else r_type
@@ -595,7 +600,10 @@ def binary_operation_sql(
595
600
  return _operator_map_sql(operator, left, right, l_type, r_type)
596
601
  except KeyError:
597
602
  try:
598
- return _operator_map_sql(operator, right, left, r_type, l_type)
603
+ sql, type_, switch = _operator_map_sql(
604
+ operator, right, left, r_type, l_type
605
+ )
606
+ return sql, type_, not switch
599
607
  except KeyError:
600
608
  pass
601
609
  raise OperatorNotFoundError(operator, (l_type, r_type), (left, right)) from None
@@ -852,13 +852,13 @@ class SQLInterpreter(Interpreter):
852
852
  cast = "iprange"
853
853
  return (f"%s::{cast}", [token.real_value], type(token.real_value))
854
854
 
855
- def neg(self, tree: Tree) -> tuple[str, list[Any], Any]:
855
+ def neg(self, tree: Tree) -> tuple[str, list, Any]:
856
856
  sql, params, type_ = self.visit(tree)
857
- return (f"-{sql}", params, type_)
857
+ return (f"(-{sql})", params, type_)
858
858
 
859
859
  def range_op(
860
860
  self, l_tree: Tree, r_tree: Tree
861
- ) -> tuple[str, list[Any], type[tuple] | type[IP]]:
861
+ ) -> tuple[str, list, type[tuple] | type[IP]]:
862
862
  l_sql, l_params, l_type = self.visit(l_tree)
863
863
  r_sql, r_params, r_type = self.visit(r_tree)
864
864
 
@@ -898,7 +898,7 @@ class SQLInterpreter(Interpreter):
898
898
  tuple,
899
899
  )
900
900
 
901
- def var_from_data(self, var: TokenWrapper) -> tuple[str, list[Any], Any]:
901
+ def var_from_data(self, var: TokenWrapper) -> tuple[str, list, Any]:
902
902
  if var.real_value in self.data:
903
903
  column, type_ = self.data[var.real_value]
904
904
  return (f'"{column}"', [], type_)
@@ -912,68 +912,69 @@ class SQLInterpreter(Interpreter):
912
912
  end_pos=var.end_pos,
913
913
  )
914
914
 
915
- def _binary_operation(self, operator, l_tree, r_tree) -> tuple[str, list[Any], Any]:
915
+ def _binary_operation(self, operator, l_tree, r_tree) -> tuple[str, list, Any]:
916
916
  l_sql, l_params, l_type = self.visit(l_tree)
917
917
  r_sql, r_params, r_type = self.visit(r_tree)
918
918
 
919
- sql, type_ = binary_operation_sql(operator, l_sql, r_sql, l_type, r_type)
919
+ sql, type_, switch = binary_operation_sql(
920
+ operator, l_sql, r_sql, l_type, r_type
921
+ )
922
+ params = l_params + r_params if not switch else r_params + l_params
920
923
 
921
- return sql, l_params + r_params, type_
924
+ return sql, params, type_
922
925
 
923
- def add(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], Any]:
926
+ def add(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, Any]:
924
927
  return self._binary_operation("+", l_tree, r_tree)
925
928
 
926
- def sub(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], Any]:
929
+ def sub(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, Any]:
927
930
  return self._binary_operation("-", l_tree, r_tree)
928
931
 
929
- def mul(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], Any]:
932
+ def mul(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, Any]:
930
933
  return self._binary_operation("*", l_tree, r_tree)
931
934
 
932
- def div(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], Any]:
935
+ def div(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, Any]:
933
936
  return self._binary_operation("/", l_tree, r_tree)
934
937
 
935
- def mod(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], Any]:
938
+ def mod(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, Any]:
936
939
  return self._binary_operation("%", l_tree, r_tree)
937
940
 
938
- def eq(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], type[bool]]:
941
+ def eq(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
939
942
  return self._binary_operation("==", l_tree, r_tree)
940
943
 
941
- def in_op(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], type[bool]]:
944
+ def in_op(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
942
945
  return self._binary_operation("in", l_tree, r_tree)
943
946
 
944
- def any_eq(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], type[bool]]:
947
+ def any_eq(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
945
948
  return self._binary_operation("=", l_tree, r_tree)
946
949
 
947
- def gt(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], type[bool]]:
950
+ def gt(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
948
951
  return self._binary_operation(">", l_tree, r_tree)
949
952
 
950
- def gte(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], type[bool]]:
953
+ def gte(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
951
954
  return self._binary_operation(">=", l_tree, r_tree)
952
955
 
953
- def lt(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], type[bool]]:
956
+ def lt(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
954
957
  return self._binary_operation("<", l_tree, r_tree)
955
958
 
956
- def lte(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], type[bool]]:
959
+ def lte(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
957
960
  return self._binary_operation("<=", l_tree, r_tree)
958
961
 
959
- def or_op(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], type[bool]]:
962
+ def or_op(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
960
963
  return self._binary_operation("or", l_tree, r_tree)
961
964
 
962
- def and_op(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list[Any], type[bool]]:
965
+ def and_op(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
963
966
  return self._binary_operation("and", l_tree, r_tree)
964
967
 
965
- def not_op(self, tree: Tree) -> tuple[str, list[Any], type[bool]]:
968
+ def not_op(self, tree: Tree) -> tuple[str, list, type[bool]]:
966
969
  sql, params, type_ = self.visit(tree)
967
970
  return f"NOT {sql}", params, type_
968
971
 
969
- def contains_op(
970
- self, l_tree: Tree, r_tree: Tree
971
- ) -> tuple[str, list[Any], type[bool]]:
972
+ def contains_op(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
972
973
  return self._binary_operation("contains", l_tree, r_tree)
973
974
 
974
- def exists_op(self, path: Token) -> tuple[str, list[Any], type[bool]]:
975
+ def exists_op(self, path: Token) -> tuple[str, list, type[bool]]:
975
976
  var_sql, _, _ = self.var_from_data(TokenWrapper(path, path))
976
- return f"{var_sql} is not null", [], bool
977
+ return f"{var_sql} IS NOT NULL", [], bool
977
978
 
978
979
  def exists_with_default(self, path: Token, default: Any) -> Any:
979
980
  var_sql, _, _ = self.var_from_data(TokenWrapper(path, path))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ransacklib
3
- Version: 1.1.0.dev5
3
+ Version: 1.1.0.dev6
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
@@ -530,9 +530,9 @@ class TestSQLInterpreter:
530
530
  [
531
531
  # Numbers.
532
532
  ("5", "%s", [5]),
533
- ("-12", "-%s", [12]),
533
+ ("-12", "(-%s)", [12]),
534
534
  ("23.22", "%s", [23.22]),
535
- ("-0.224", "-%s", [0.224]),
535
+ ("-0.224", "(-%s)", [0.224]),
536
536
  ("13e19", "%s", [1.3e20]),
537
537
  # Datetime.
538
538
  ("2025-01-01", "%s::TIMESTAMP", [datetime(2025, 1, 1, 0, 0)]),
@@ -554,7 +554,7 @@ class TestSQLInterpreter:
554
554
  ),
555
555
  # Timedelta.
556
556
  ("01:23:34", "%s", [timedelta(seconds=5014)]),
557
- ("-10D23:59:59", "-%s", [timedelta(days=10, seconds=86399)]),
557
+ ("-10D23:59:59", "(-%s)", [timedelta(days=10, seconds=86399)]),
558
558
  ("99:59:59", "%s", [timedelta(days=4, seconds=14399)]),
559
559
  # Strings.
560
560
  ("''", "%s", [""]),
@@ -659,6 +659,109 @@ class TestSQLInterpreter:
659
659
  ),
660
660
  [],
661
661
  ),
662
+ # Test arithmetic operations.
663
+ (
664
+ "(10 + 12 - 7) * (15 / (8 % 3)) - (-5)",
665
+ "(((%s + %s) - %s) * %s / (%s % %s) - (-%s))",
666
+ [10, 12, 7, 15, 8, 3, 5],
667
+ ),
668
+ # Test comparisons.
669
+ (
670
+ "10 > 12 and 12 < 20 and (17 >= 12 or 12 <= 1) and not 53 == 13",
671
+ "%s > %s AND %s < %s AND (%s >= %s OR %s <= %s) AND NOT %s = %s",
672
+ [10, 12, 12, 20, 17, 12, 12, 1, 53, 13],
673
+ ),
674
+ # Test exists operators.
675
+ ("a_int??", '"a" IS NOT NULL', []),
676
+ ("a_int??14", 'COALESCE("a", %s)', [14]),
677
+ # Test contains operator.
678
+ ("'abcdfg' contains 'bcd'", "position(%s in %s)>0", ["bcd", "abcdfg"]),
679
+ # Test = operator.
680
+ ("1 = 1", "%s = %s", [1, 1]),
681
+ ("1 = [1, 2, 3]", "%s = ANY(ARRAY[%s, %s, %s])", [1, 1, 2, 3]),
682
+ ("[1, 2, 3] = 1", "%s = ANY(ARRAY[%s, %s, %s])", [1, 1, 2, 3]),
683
+ ("[1, 2] = [3, 4]", "ARRAY[%s, %s] && ARRAY[%s, %s]", [1, 2, 3, 4]),
684
+ (
685
+ "2 = 1..10",
686
+ (
687
+ "CASE WHEN %s < %s "
688
+ "THEN int4range(%s, %s, '[]') "
689
+ "ELSE int4range(%s, %s, '[]') END @> %s"
690
+ ),
691
+ [1, 10, 1, 10, 10, 1, 2],
692
+ ),
693
+ (
694
+ "1..10 = 2",
695
+ (
696
+ "CASE WHEN %s < %s "
697
+ "THEN int4range(%s, %s, '[]') "
698
+ "ELSE int4range(%s, %s, '[]') END @> %s"
699
+ ),
700
+ [1, 10, 1, 10, 10, 1, 2],
701
+ ),
702
+ (
703
+ "a_int..b_int = c_int..d_int",
704
+ (
705
+ 'CASE WHEN "a" < "b" '
706
+ 'THEN int4range("a", "b", \'[]\') '
707
+ 'ELSE int4range("b", "a", \'[]\') END '
708
+ '&& CASE WHEN "c" < "d" '
709
+ 'THEN int4range("c", "d", \'[]\') '
710
+ 'ELSE int4range("d", "c", \'[]\') END'
711
+ ),
712
+ [],
713
+ ),
714
+ (
715
+ "10.10.10.10 = 10.10.10.10",
716
+ "%s::ipaddress && %s::ipaddress",
717
+ [from_str("10.10.10.10"), from_str("10.10.10.10")],
718
+ ),
719
+ (
720
+ "10.10.10.10 = 10.10.10.0 .. 10.10.10.20",
721
+ "%s::ipaddress && iprange(%s::ipaddress, %s::ipaddress)",
722
+ [
723
+ from_str("10.10.10.10"),
724
+ from_str("10.10.10.0"),
725
+ from_str("10.10.10.20"),
726
+ ],
727
+ ),
728
+ (
729
+ "192.168.0.5 = 192.168.0.0-192.168.0.255",
730
+ "%s::ipaddress && %s::iprange",
731
+ [from_str("192.168.0.5"), from_str("192.168.0.0-192.168.0.255")],
732
+ ),
733
+ (
734
+ "192.168.0.12 = 192.168.1.0/24",
735
+ "%s::ipaddress && %s::iprange",
736
+ [from_str("192.168.0.12"), from_str("192.168.1.0/24")],
737
+ ),
738
+ (
739
+ "10.10.10.10-10.10.10.20 = 10.10.10.15-10.10.10.25",
740
+ "%s::iprange && %s::iprange",
741
+ [
742
+ from_str("10.10.10.10-10.10.10.20"),
743
+ from_str("10.10.10.15-10.10.10.25"),
744
+ ],
745
+ ),
746
+ (
747
+ "10.10.10.10-10.10.10.20 = 192.168.0.0/16",
748
+ "%s::iprange && %s::iprange",
749
+ [from_str("10.10.10.10-10.10.10.20"), from_str("192.168.0.0/16")],
750
+ ),
751
+ (
752
+ "192.168.1.0/24 = 192.168.0.0/16",
753
+ "%s::iprange && %s::iprange",
754
+ [from_str("192.168.1.0/24"), from_str("192.168.0.0/16")],
755
+ ),
756
+ (
757
+ "192.168.1.10 = [192.168.0.0/24, 192.168.0.0/16]",
758
+ ("%s::ipaddress && ANY(ARRAY[%s::iprange, %s::iprange])"),
759
+ [
760
+ from_str("192.168.1.10"),
761
+ from_str("192.168.0.0/24"),
762
+ from_str("192.168.0.0/16"),
763
+ ],
764
+ ),
662
765
  ],
663
766
  )
664
767
  def test_parse_sql(self, parser, sql, expr, expected_sql, expected_params):
File without changes