sql_fusion 1.0.0__py3-none-any.whl
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.
- sql_fusion/__init__.py +15 -0
- sql_fusion/composite_table.py +689 -0
- sql_fusion/operators.py +119 -0
- sql_fusion/query/__init__.py +0 -0
- sql_fusion/query/delete.py +84 -0
- sql_fusion/query/insert.py +72 -0
- sql_fusion/query/select.py +527 -0
- sql_fusion/query/update.py +76 -0
- sql_fusion-1.0.0.dist-info/METADATA +580 -0
- sql_fusion-1.0.0.dist-info/RECORD +12 -0
- sql_fusion-1.0.0.dist-info/WHEEL +4 -0
- sql_fusion-1.0.0.dist-info/entry_points.txt +3 -0
sql_fusion/operators.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from typing import Any, Callable
|
|
2
|
+
|
|
3
|
+
OPERATORS: dict[str, type[AbstractOperator]] = {}
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AbstractOperator:
|
|
7
|
+
sql_symbol: str = ""
|
|
8
|
+
|
|
9
|
+
def __init__(self, col_ref: str) -> None:
|
|
10
|
+
self._col_ref: str = col_ref
|
|
11
|
+
|
|
12
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
13
|
+
raise NotImplementedError()
|
|
14
|
+
|
|
15
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
16
|
+
raise NotImplementedError()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def register_operator(
|
|
20
|
+
symbol: str,
|
|
21
|
+
) -> Callable[[type[AbstractOperator]], type[AbstractOperator]]:
|
|
22
|
+
def decorator(cls: type[AbstractOperator]) -> type[AbstractOperator]:
|
|
23
|
+
OPERATORS[symbol] = cls
|
|
24
|
+
cls.sql_symbol = symbol
|
|
25
|
+
return cls
|
|
26
|
+
|
|
27
|
+
return decorator
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@register_operator("=")
|
|
31
|
+
class EqualOperator(AbstractOperator):
|
|
32
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
33
|
+
return f"{self._col_ref} = ?", (value,)
|
|
34
|
+
|
|
35
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
36
|
+
return f"{self._col_ref} = {value_ref}", tuple()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@register_operator("!=")
|
|
40
|
+
class NotEqualOperator(AbstractOperator):
|
|
41
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
42
|
+
return f"{self._col_ref} != ?", (value,)
|
|
43
|
+
|
|
44
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
45
|
+
return f"{self._col_ref} != {value_ref}", tuple()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@register_operator("<")
|
|
49
|
+
class LessThanOperator(AbstractOperator):
|
|
50
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
51
|
+
return f"{self._col_ref} < ?", (value,)
|
|
52
|
+
|
|
53
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
54
|
+
return f"{self._col_ref} < {value_ref}", tuple()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@register_operator(">")
|
|
58
|
+
class GreaterThanOperator(AbstractOperator):
|
|
59
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
60
|
+
return f"{self._col_ref} > ?", (value,)
|
|
61
|
+
|
|
62
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
63
|
+
return f"{self._col_ref} > {value_ref}", tuple()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@register_operator("<=")
|
|
67
|
+
class LessThanOrEqualOperator(AbstractOperator):
|
|
68
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
69
|
+
return f"{self._col_ref} <= ?", (value,)
|
|
70
|
+
|
|
71
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
72
|
+
return f"{self._col_ref} <= {value_ref}", tuple()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@register_operator(">=")
|
|
76
|
+
class GreaterThanOrEqualOperator(AbstractOperator):
|
|
77
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
78
|
+
return f"{self._col_ref} >= ?", (value,)
|
|
79
|
+
|
|
80
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
81
|
+
return f"{self._col_ref} >= {value_ref}", tuple()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@register_operator("LIKE")
|
|
85
|
+
class LikeOperator(AbstractOperator):
|
|
86
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
87
|
+
return f"{self._col_ref} LIKE ?", (value,)
|
|
88
|
+
|
|
89
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
90
|
+
return f"{self._col_ref} LIKE {value_ref}", tuple()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@register_operator("ILIKE")
|
|
94
|
+
class IlikeOperator(AbstractOperator):
|
|
95
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
96
|
+
return f"{self._col_ref} ILIKE ?", (value,)
|
|
97
|
+
|
|
98
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
99
|
+
return f"{self._col_ref} ILIKE {value_ref}", tuple()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@register_operator("IN")
|
|
103
|
+
class InOperator(AbstractOperator):
|
|
104
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
105
|
+
placeholders: str = ", ".join("?" * len(value))
|
|
106
|
+
return f"{self._col_ref} IN ({placeholders})", tuple(value)
|
|
107
|
+
|
|
108
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
109
|
+
return f"{self._col_ref} IN ({value_ref})", tuple()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@register_operator("NOT IN")
|
|
113
|
+
class NotInOperator(AbstractOperator):
|
|
114
|
+
def to_sql(self, value: Any) -> tuple[str, tuple[Any, ...]]:
|
|
115
|
+
placeholders: str = ", ".join("?" * len(value))
|
|
116
|
+
return f"{self._col_ref} NOT IN ({placeholders})", tuple(value)
|
|
117
|
+
|
|
118
|
+
def to_sql_ref(self, value_ref: str) -> tuple[str, tuple[Any, ...]]:
|
|
119
|
+
return f"{self._col_ref} NOT IN ({value_ref})", tuple()
|
|
File without changes
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from copy import copy
|
|
2
|
+
from typing import Any, Self
|
|
3
|
+
|
|
4
|
+
from sql_fusion.composite_table import (
|
|
5
|
+
AbstractQuery,
|
|
6
|
+
AliasRegistry,
|
|
7
|
+
Column,
|
|
8
|
+
FunctionCall,
|
|
9
|
+
Table,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class delete(AbstractQuery):
|
|
14
|
+
def __init__(self, table: Table | None = None) -> None:
|
|
15
|
+
super().__init__(table=table, columns=())
|
|
16
|
+
self._returning_columns: tuple[Column | FunctionCall, ...] = ()
|
|
17
|
+
self._returning_all: bool = False
|
|
18
|
+
|
|
19
|
+
def returning(self, *columns: Column | FunctionCall) -> Self:
|
|
20
|
+
if not columns:
|
|
21
|
+
self._returning_all = True
|
|
22
|
+
self._returning_columns = ()
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
if not self._returning_all:
|
|
26
|
+
self._returning_columns += columns
|
|
27
|
+
|
|
28
|
+
return self
|
|
29
|
+
|
|
30
|
+
def build_query(
|
|
31
|
+
self,
|
|
32
|
+
alias_registry: AliasRegistry | None = None,
|
|
33
|
+
) -> tuple[str, tuple[Any, ...]]:
|
|
34
|
+
registry = alias_registry or self._alias_registry
|
|
35
|
+
table = self._get_table()
|
|
36
|
+
alias = registry.get_alias_for_table(table)
|
|
37
|
+
with_sql, with_params = self._build_with_clause(registry)
|
|
38
|
+
from_clause = self._build_clause(
|
|
39
|
+
"FROM",
|
|
40
|
+
"FROM",
|
|
41
|
+
f'"{table.get_name()}" AS "{alias.name}"',
|
|
42
|
+
)
|
|
43
|
+
query = self._build_clause("DELETE", "DELETE", from_clause)
|
|
44
|
+
params: list[Any] = list(with_params)
|
|
45
|
+
|
|
46
|
+
if self._where_condition:
|
|
47
|
+
where_sql, where_params = self._where_condition.to_sql(registry)
|
|
48
|
+
query += f" {self._build_clause('WHERE', 'WHERE', where_sql)}"
|
|
49
|
+
params.extend(where_params)
|
|
50
|
+
|
|
51
|
+
if self._returning_all:
|
|
52
|
+
query += f" {self._build_clause('RETURNING', 'RETURNING', '*')}"
|
|
53
|
+
return self._apply_compile_expressions(
|
|
54
|
+
f"{with_sql} {query}" if with_sql else query,
|
|
55
|
+
tuple(params),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if self._returning_columns:
|
|
59
|
+
returning_parts: list[str] = []
|
|
60
|
+
|
|
61
|
+
for col in self._returning_columns:
|
|
62
|
+
if isinstance(col, FunctionCall):
|
|
63
|
+
func_sql, func_params = col.to_sql(registry)
|
|
64
|
+
returning_parts.append(func_sql)
|
|
65
|
+
params.extend(func_params)
|
|
66
|
+
else:
|
|
67
|
+
alias = registry.get_alias_for_table(col.table)
|
|
68
|
+
returning_parts.append(f'"{alias.name}"."{col.name}"')
|
|
69
|
+
|
|
70
|
+
query += " " + self._build_clause(
|
|
71
|
+
"RETURNING",
|
|
72
|
+
"RETURNING",
|
|
73
|
+
", ".join(returning_parts),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return self._apply_compile_expressions(
|
|
77
|
+
f"{with_sql} {query}" if with_sql else query,
|
|
78
|
+
tuple(params),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def from_(self, table: Table | AbstractQuery) -> Self:
|
|
82
|
+
qs = copy(self)
|
|
83
|
+
qs._table = table if isinstance(table, Table) else Table(table)
|
|
84
|
+
return qs
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from typing import Any, Self
|
|
2
|
+
|
|
3
|
+
from sql_fusion.composite_table import AbstractQuery, AliasRegistry, Table
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class insert(AbstractQuery):
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
table: Table,
|
|
10
|
+
*,
|
|
11
|
+
or_replace: bool = False,
|
|
12
|
+
or_ignore: bool = False,
|
|
13
|
+
) -> None:
|
|
14
|
+
super().__init__(table=table, columns=())
|
|
15
|
+
self._values: dict[str, Any] = {}
|
|
16
|
+
self._or_replace: bool = or_replace
|
|
17
|
+
self._or_ignore: bool = or_ignore
|
|
18
|
+
|
|
19
|
+
def values(self, **values: Any) -> Self:
|
|
20
|
+
if not values:
|
|
21
|
+
raise ValueError("No values provided for insert")
|
|
22
|
+
self._values.update(values)
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
def build_query(
|
|
26
|
+
self,
|
|
27
|
+
alias_registry: AliasRegistry | None = None,
|
|
28
|
+
) -> tuple[str, tuple[Any, ...]]:
|
|
29
|
+
if not self._values:
|
|
30
|
+
raise ValueError("No values provided for insert")
|
|
31
|
+
registry = alias_registry or self._alias_registry
|
|
32
|
+
table = self._get_table()
|
|
33
|
+
with_sql, with_params = self._build_with_clause(registry)
|
|
34
|
+
|
|
35
|
+
columns = list(self._values.keys())
|
|
36
|
+
col_names = ", ".join(f'"{col}"' for col in columns)
|
|
37
|
+
placeholders = ", ".join("?" * len(columns))
|
|
38
|
+
params = tuple(self._values[col] for col in columns)
|
|
39
|
+
|
|
40
|
+
insert_stmnt = "INSERT"
|
|
41
|
+
|
|
42
|
+
if self._or_replace and self._or_ignore:
|
|
43
|
+
raise ValueError("Cannot use both or_replace and or_ignore")
|
|
44
|
+
if self._or_replace:
|
|
45
|
+
insert_stmnt += " OR REPLACE"
|
|
46
|
+
elif self._or_ignore:
|
|
47
|
+
insert_stmnt += " OR IGNORE"
|
|
48
|
+
|
|
49
|
+
into_clause = self._build_clause(
|
|
50
|
+
"INTO",
|
|
51
|
+
"INTO",
|
|
52
|
+
f'"{table.get_name()}" ({col_names})',
|
|
53
|
+
)
|
|
54
|
+
values_clause = self._build_clause(
|
|
55
|
+
"VALUES",
|
|
56
|
+
"VALUES",
|
|
57
|
+
f"({placeholders})",
|
|
58
|
+
)
|
|
59
|
+
query = self._build_clause(
|
|
60
|
+
"INSERT",
|
|
61
|
+
insert_stmnt,
|
|
62
|
+
f"{into_clause} {values_clause}",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
query_parts = [query]
|
|
66
|
+
if with_sql:
|
|
67
|
+
query_parts.insert(0, with_sql)
|
|
68
|
+
|
|
69
|
+
return self._apply_compile_expressions(
|
|
70
|
+
" ".join(query_parts),
|
|
71
|
+
tuple(with_params) + params,
|
|
72
|
+
)
|