sql_fusion 1.1.0__tar.gz → 1.2.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sql_fusion
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Python query builder with a focus on composability and reusability.
5
5
  Author: Mastermind-U
6
6
  Author-email: Mastermind-U <rex49513@gmail.com>
@@ -77,6 +77,7 @@ In short, the goal is to keep the ergonomics of a lightweight builder while stil
77
77
  - automatic table aliases
78
78
  - composable conditions with `AND`, `OR`, and `NOT`
79
79
  - joins, subqueries, and CTEs
80
+ - set operations with `UNION`, `INTERSECT`, and `EXCEPT`
80
81
  - ordering and grouping with `GROUP BY`, `ROLLUP`, `CUBE`, and `GROUPING SETS`
81
82
  - aggregate and custom SQL functions through `func`
82
83
  - backend-specific SQL rewrites through compile expressions
@@ -113,10 +114,13 @@ from sql_fusion import (
113
114
  Column,
114
115
  Table,
115
116
  delete,
117
+ except_,
116
118
  func,
117
119
  insert,
120
+ intersect,
118
121
  select,
119
- text,
122
+ union,
123
+ text_op,
120
124
  update,
121
125
  )
122
126
  ```
@@ -63,6 +63,7 @@ In short, the goal is to keep the ergonomics of a lightweight builder while stil
63
63
  - automatic table aliases
64
64
  - composable conditions with `AND`, `OR`, and `NOT`
65
65
  - joins, subqueries, and CTEs
66
+ - set operations with `UNION`, `INTERSECT`, and `EXCEPT`
66
67
  - ordering and grouping with `GROUP BY`, `ROLLUP`, `CUBE`, and `GROUPING SETS`
67
68
  - aggregate and custom SQL functions through `func`
68
69
  - backend-specific SQL rewrites through compile expressions
@@ -99,10 +100,13 @@ from sql_fusion import (
99
100
  Column,
100
101
  Table,
101
102
  delete,
103
+ except_,
102
104
  func,
103
105
  insert,
106
+ intersect,
104
107
  select,
105
- text,
108
+ union,
109
+ text_op,
106
110
  update,
107
111
  )
108
112
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sql_fusion"
3
- version = "1.1.0"
3
+ version = "1.2.0"
4
4
  description = "Python query builder with a focus on composability and reusability."
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Mastermind-U", email = "rex49513@gmail.com" }]
@@ -2,6 +2,7 @@ from .composite_table import Alias, Column, Table, func, text_op
2
2
  from .query.delete import delete
3
3
  from .query.insert import insert
4
4
  from .query.select import select
5
+ from .query.sets import except_, intersect, union
5
6
  from .query.update import update
6
7
 
7
8
  __all__ = [
@@ -9,9 +10,12 @@ __all__ = [
9
10
  "Column",
10
11
  "Table",
11
12
  "delete",
13
+ "except_",
12
14
  "func",
13
15
  "insert",
16
+ "intersect",
14
17
  "select",
15
18
  "text_op",
19
+ "union",
16
20
  "update",
17
21
  ]
@@ -511,7 +511,8 @@ class Condition:
511
511
  )
512
512
  sql, op_params = operator.to_sql_ref(subquery_sql)
513
513
  return apply_negation(
514
- sql, col_params + subquery_params + op_params
514
+ sql,
515
+ col_params + subquery_params + op_params,
515
516
  )
516
517
 
517
518
  sql, op_params = operator.to_sql(self.value)
@@ -0,0 +1,15 @@
1
+ from .delete import delete
2
+ from .insert import insert
3
+ from .select import select
4
+ from .sets import except_, intersect, union
5
+ from .update import update
6
+
7
+ __all__ = [
8
+ "delete",
9
+ "except_",
10
+ "insert",
11
+ "intersect",
12
+ "select",
13
+ "union",
14
+ "update",
15
+ ]
@@ -0,0 +1,107 @@
1
+ from typing import Any
2
+
3
+ from sql_fusion.composite_table import AbstractQuery, AliasRegistry
4
+
5
+
6
+ class _set_operation(AbstractQuery):
7
+ def __init__(
8
+ self,
9
+ query1: AbstractQuery,
10
+ query2: AbstractQuery,
11
+ ) -> None:
12
+ super().__init__(table=None, columns=())
13
+ self._query1 = query1
14
+ self._query2 = query2
15
+
16
+ def _operator_sql(self) -> str:
17
+ raise NotImplementedError()
18
+
19
+ def _render_query(
20
+ self,
21
+ query: AbstractQuery,
22
+ alias_registry: AliasRegistry,
23
+ ) -> tuple[str, tuple[Any, ...]]:
24
+ return query.build_query(alias_registry)
25
+
26
+ def build_query(
27
+ self,
28
+ alias_registry: AliasRegistry | None = None,
29
+ ) -> tuple[str, tuple[Any, ...]]:
30
+ registry = alias_registry or self._alias_registry
31
+ params: list[Any] = []
32
+
33
+ with_sql, with_params = self._build_with_clause(registry)
34
+ params.extend(with_params)
35
+
36
+ left_sql, left_params = self._render_query(self._query1, registry)
37
+ right_sql, right_params = self._render_query(self._query2, registry)
38
+
39
+ query_parts: list[str] = []
40
+ if with_sql:
41
+ query_parts.append(with_sql)
42
+ query_parts.append(
43
+ f"{left_sql} {self._operator_sql()} {right_sql}",
44
+ )
45
+
46
+ params.extend(left_params)
47
+ params.extend(right_params)
48
+
49
+ return self._apply_compile_expressions(
50
+ " ".join(query_parts),
51
+ tuple(params),
52
+ )
53
+
54
+
55
+ class union(_set_operation):
56
+ def __init__(
57
+ self,
58
+ query1: AbstractQuery,
59
+ query2: AbstractQuery,
60
+ all: bool = False, # noqa: A002
61
+ by_name: bool = False,
62
+ ) -> None:
63
+ super().__init__(query1, query2)
64
+ self._all = all
65
+ self._by_name = by_name
66
+
67
+ def _operator_sql(self) -> str:
68
+ operator = "UNION"
69
+ if self._all:
70
+ operator += " ALL"
71
+ if self._by_name:
72
+ operator += " BY NAME"
73
+ return operator
74
+
75
+
76
+ class intersect(_set_operation):
77
+ def __init__(
78
+ self,
79
+ query1: AbstractQuery,
80
+ query2: AbstractQuery,
81
+ all_: bool = False,
82
+ ) -> None:
83
+ super().__init__(query1, query2)
84
+ self._all = all_
85
+
86
+ def _operator_sql(self) -> str:
87
+ operator = "INTERSECT"
88
+ if self._all:
89
+ operator += " ALL"
90
+ return operator
91
+
92
+
93
+ class except_(_set_operation):
94
+ def __init__(
95
+ self,
96
+ query1: AbstractQuery,
97
+ query2: AbstractQuery,
98
+ all_: bool = False,
99
+ ) -> None:
100
+ super().__init__(query1, query2)
101
+ self._all = all_
102
+
103
+ def _operator_sql(self) -> str:
104
+ operator = "EXCEPT"
105
+ if self._all:
106
+ operator += " ALL"
107
+ return operator
File without changes