sqlqb 0.1.3__tar.gz → 0.1.4__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.
- sqlqb-0.1.4/.envrc +3 -0
- sqlqb-0.1.4/.pythonrc.py +1 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/PKG-INFO +1 -1
- {sqlqb-0.1.3 → sqlqb-0.1.4}/pyproject.toml +1 -1
- {sqlqb-0.1.3 → sqlqb-0.1.4}/src/sqlqb/delete.py +15 -3
- {sqlqb-0.1.3 → sqlqb-0.1.4}/src/sqlqb/pymysql/__init__.py +21 -6
- {sqlqb-0.1.3 → sqlqb-0.1.4}/src/sqlqb/select.py +29 -7
- {sqlqb-0.1.3 → sqlqb-0.1.4}/src/sqlqb/sqlalchemy/__init__.py +4 -2
- {sqlqb-0.1.3 → sqlqb-0.1.4}/src/sqlqb/update.py +21 -4
- sqlqb-0.1.3/tests/test_sqlite.py → sqlqb-0.1.4/tests/test_sqlite3.py +4 -4
- {sqlqb-0.1.3 → sqlqb-0.1.4}/uv.lock +1 -1
- {sqlqb-0.1.3 → sqlqb-0.1.4}/.gitignore +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/Makefile +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/README.md +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/data.db +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/src/sqlqb/__init__.py +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/src/sqlqb/insert.py +0 -0
- {sqlqb-0.1.3/src/sqlqb/sqlite → sqlqb-0.1.4/src/sqlqb/sqlite3}/__init__.py +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/tests/__init__.py +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/tests/test_delete.py +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/tests/test_insert.py +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/tests/test_pymysql.py +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/tests/test_select.py +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/tests/test_sqlalchemy.py +0 -0
- {sqlqb-0.1.3 → sqlqb-0.1.4}/tests/test_update.py +0 -0
sqlqb-0.1.4/.envrc
ADDED
sqlqb-0.1.4/.pythonrc.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from sqlqb import * # noqa: F403
|
|
@@ -6,6 +6,7 @@ class Delete:
|
|
|
6
6
|
self.__table: str | None = None
|
|
7
7
|
self.__wheres: list[str] = []
|
|
8
8
|
self.__params: list = []
|
|
9
|
+
self.__named_params: dict = {}
|
|
9
10
|
self.__limit: int | None = None
|
|
10
11
|
|
|
11
12
|
def From(self, table: str) -> "Delete":
|
|
@@ -14,9 +15,18 @@ class Delete:
|
|
|
14
15
|
self.__table = table
|
|
15
16
|
return self
|
|
16
17
|
|
|
17
|
-
def Where(self, condition: str, *args) -> "Delete":
|
|
18
|
+
def Where(self, condition: str, *args, **kwargs) -> "Delete":
|
|
19
|
+
if args and kwargs:
|
|
20
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
21
|
+
if args and self.__named_params:
|
|
22
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
23
|
+
if kwargs and self.__params:
|
|
24
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
18
25
|
self.__wheres.append(condition)
|
|
19
|
-
|
|
26
|
+
if kwargs:
|
|
27
|
+
self.__named_params.update(kwargs)
|
|
28
|
+
else:
|
|
29
|
+
self.__params += list(args)
|
|
20
30
|
return self
|
|
21
31
|
|
|
22
32
|
def Limit(self, limit: int) -> "Delete":
|
|
@@ -24,7 +34,9 @@ class Delete:
|
|
|
24
34
|
return self
|
|
25
35
|
|
|
26
36
|
@property
|
|
27
|
-
def params(self) -> list:
|
|
37
|
+
def params(self) -> list | dict:
|
|
38
|
+
if self.__named_params:
|
|
39
|
+
return self.__named_params
|
|
28
40
|
return self.__params
|
|
29
41
|
|
|
30
42
|
@property
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
1
3
|
from pymysql.connections import Connection as _Connection
|
|
2
4
|
from pymysql.cursors import DictCursor
|
|
3
5
|
|
|
@@ -7,25 +9,35 @@ from sqlqb import Update as _Update
|
|
|
7
9
|
from sqlqb import Delete as _Delete
|
|
8
10
|
|
|
9
11
|
|
|
12
|
+
def _to_pymysql(sql: str, params: list | dict) -> tuple[str, list | dict]:
|
|
13
|
+
"""Convert :name placeholders to %(name)s style for PyMySQL when params is a dict."""
|
|
14
|
+
if isinstance(params, dict):
|
|
15
|
+
sql = re.sub(r":([a-zA-Z_][a-zA-Z0-9_]*)", r"%(\1)s", sql)
|
|
16
|
+
return sql, params
|
|
17
|
+
|
|
18
|
+
|
|
10
19
|
class Select(_Select):
|
|
11
20
|
def __init__(self, connection: _Connection, *args):
|
|
12
21
|
super().__init__(*args)
|
|
13
22
|
self.__connection = connection
|
|
14
23
|
|
|
15
24
|
def execute(self):
|
|
25
|
+
sql, params = _to_pymysql(self.sql, self.params)
|
|
16
26
|
cursor = self.__connection.cursor()
|
|
17
|
-
cursor.execute(
|
|
27
|
+
cursor.execute(sql, params)
|
|
18
28
|
return cursor
|
|
19
29
|
|
|
20
30
|
def fetchone(self):
|
|
31
|
+
sql, params = _to_pymysql(self.sql, self.params)
|
|
21
32
|
with self.__connection.cursor() as cursor:
|
|
22
|
-
cursor.execute(
|
|
33
|
+
cursor.execute(sql, params)
|
|
23
34
|
result = cursor.fetchone()
|
|
24
35
|
return result
|
|
25
36
|
|
|
26
37
|
def fetchall(self):
|
|
38
|
+
sql, params = _to_pymysql(self.sql, self.params)
|
|
27
39
|
with self.__connection.cursor() as cursor:
|
|
28
|
-
cursor.execute(
|
|
40
|
+
cursor.execute(sql, params)
|
|
29
41
|
result = cursor.fetchall()
|
|
30
42
|
return result
|
|
31
43
|
|
|
@@ -36,8 +48,9 @@ class Insert(_Insert):
|
|
|
36
48
|
self.__connection = connection
|
|
37
49
|
|
|
38
50
|
def execute(self) -> int:
|
|
51
|
+
sql, params = _to_pymysql(self.sql, self.params)
|
|
39
52
|
with self.__connection.cursor() as cursor:
|
|
40
|
-
cursor.execute(
|
|
53
|
+
cursor.execute(sql, params)
|
|
41
54
|
return cursor.rowcount
|
|
42
55
|
|
|
43
56
|
|
|
@@ -47,8 +60,9 @@ class Update(_Update):
|
|
|
47
60
|
self.__connection = connection
|
|
48
61
|
|
|
49
62
|
def execute(self) -> int:
|
|
63
|
+
sql, params = _to_pymysql(self.sql, self.params)
|
|
50
64
|
with self.__connection.cursor() as cursor:
|
|
51
|
-
cursor.execute(
|
|
65
|
+
cursor.execute(sql, params)
|
|
52
66
|
return cursor.rowcount
|
|
53
67
|
|
|
54
68
|
|
|
@@ -58,8 +72,9 @@ class Delete(_Delete):
|
|
|
58
72
|
self.__connection = connection
|
|
59
73
|
|
|
60
74
|
def execute(self) -> int:
|
|
75
|
+
sql, params = _to_pymysql(self.sql, self.params)
|
|
61
76
|
with self.__connection.cursor() as cursor:
|
|
62
|
-
cursor.execute(
|
|
77
|
+
cursor.execute(sql, params)
|
|
63
78
|
return cursor.rowcount
|
|
64
79
|
|
|
65
80
|
|
|
@@ -7,6 +7,7 @@ class Select:
|
|
|
7
7
|
self.__table: str | None = None
|
|
8
8
|
self.__wheres: list[str] = []
|
|
9
9
|
self.__params: list = []
|
|
10
|
+
self.__named_params: dict = {}
|
|
10
11
|
self.__joins: list[str] = []
|
|
11
12
|
self.__orderby: list[tuple[str, str]] = []
|
|
12
13
|
self.__groupby: list[str] = []
|
|
@@ -25,14 +26,32 @@ class Select:
|
|
|
25
26
|
self.__table = table
|
|
26
27
|
return self
|
|
27
28
|
|
|
28
|
-
def Join(self, join: str, *args) -> "Select":
|
|
29
|
+
def Join(self, join: str, *args, **kwargs) -> "Select":
|
|
30
|
+
if args and kwargs:
|
|
31
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
32
|
+
if args and self.__named_params:
|
|
33
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
34
|
+
if kwargs and self.__params:
|
|
35
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
29
36
|
self.__joins.append(join)
|
|
30
|
-
|
|
37
|
+
if kwargs:
|
|
38
|
+
self.__named_params.update(kwargs)
|
|
39
|
+
else:
|
|
40
|
+
self.__params += list(args)
|
|
31
41
|
return self
|
|
32
42
|
|
|
33
|
-
def Where(self, condition: str, *args) -> "Select":
|
|
43
|
+
def Where(self, condition: str, *args, **kwargs) -> "Select":
|
|
44
|
+
if args and kwargs:
|
|
45
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
46
|
+
if args and self.__named_params:
|
|
47
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
48
|
+
if kwargs and self.__params:
|
|
49
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
34
50
|
self.__wheres.append(condition)
|
|
35
|
-
|
|
51
|
+
if kwargs:
|
|
52
|
+
self.__named_params.update(kwargs)
|
|
53
|
+
else:
|
|
54
|
+
self.__params += list(args)
|
|
36
55
|
return self
|
|
37
56
|
|
|
38
57
|
def OrderBy(self, column: str, direction: str = "ASC") -> "Select":
|
|
@@ -54,7 +73,9 @@ class Select:
|
|
|
54
73
|
return self
|
|
55
74
|
|
|
56
75
|
@property
|
|
57
|
-
def params(self) -> list:
|
|
76
|
+
def params(self) -> list | dict:
|
|
77
|
+
if self.__named_params:
|
|
78
|
+
return self.__named_params
|
|
58
79
|
return self.__params
|
|
59
80
|
|
|
60
81
|
@property
|
|
@@ -62,8 +83,9 @@ class Select:
|
|
|
62
83
|
sql = io.StringIO()
|
|
63
84
|
sql.write("SELECT ")
|
|
64
85
|
sql.write(", ".join(self.__columns or "*"))
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
if self.__table:
|
|
87
|
+
sql.write(" FROM ")
|
|
88
|
+
sql.write(self.__table)
|
|
67
89
|
if self.__joins:
|
|
68
90
|
sql.write(" ")
|
|
69
91
|
sql.write(" ".join(self.__joins))
|
|
@@ -9,8 +9,10 @@ from sqlqb import Update as _Update
|
|
|
9
9
|
from sqlqb import Delete as _Delete
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def _bind(sql: str, params: list) -> tuple[str, dict]:
|
|
13
|
-
"""
|
|
12
|
+
def _bind(sql: str, params: list | dict) -> tuple[str, dict]:
|
|
13
|
+
"""Convert params for SQLAlchemy text(): pass dicts through, replace ? with :p0/:p1/... for lists."""
|
|
14
|
+
if isinstance(params, dict):
|
|
15
|
+
return sql, params
|
|
14
16
|
named: dict = {}
|
|
15
17
|
for i, val in enumerate(params):
|
|
16
18
|
key = f"p{i}"
|
|
@@ -9,6 +9,7 @@ class Update:
|
|
|
9
9
|
self.__sets: dict[str, object] = {}
|
|
10
10
|
self.__wheres: list[str] = []
|
|
11
11
|
self.__where_params: list = []
|
|
12
|
+
self.__where_named_params: dict = {}
|
|
12
13
|
self.__limit: int | None = None
|
|
13
14
|
|
|
14
15
|
def Set(self, **kwargs) -> "Update":
|
|
@@ -17,9 +18,18 @@ class Update:
|
|
|
17
18
|
self.__sets.update(kwargs)
|
|
18
19
|
return self
|
|
19
20
|
|
|
20
|
-
def Where(self, condition: str, *args) -> "Update":
|
|
21
|
+
def Where(self, condition: str, *args, **kwargs) -> "Update":
|
|
22
|
+
if args and kwargs:
|
|
23
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
24
|
+
if args and self.__where_named_params:
|
|
25
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
26
|
+
if kwargs and self.__where_params:
|
|
27
|
+
raise ValueError("error: cannot mix positional and named params")
|
|
21
28
|
self.__wheres.append(condition)
|
|
22
|
-
|
|
29
|
+
if kwargs:
|
|
30
|
+
self.__where_named_params.update(kwargs)
|
|
31
|
+
else:
|
|
32
|
+
self.__where_params += list(args)
|
|
23
33
|
return self
|
|
24
34
|
|
|
25
35
|
def Limit(self, limit: int) -> "Update":
|
|
@@ -27,9 +37,13 @@ class Update:
|
|
|
27
37
|
return self
|
|
28
38
|
|
|
29
39
|
@property
|
|
30
|
-
def params(self) -> list:
|
|
40
|
+
def params(self) -> list | dict:
|
|
31
41
|
if not self.__sets:
|
|
32
42
|
return []
|
|
43
|
+
if self.__where_named_params:
|
|
44
|
+
named = dict(self.__sets)
|
|
45
|
+
named.update(self.__where_named_params)
|
|
46
|
+
return named
|
|
33
47
|
return list(self.__sets.values()) + self.__where_params
|
|
34
48
|
|
|
35
49
|
@property
|
|
@@ -40,7 +54,10 @@ class Update:
|
|
|
40
54
|
sql.write("UPDATE ")
|
|
41
55
|
sql.write(self.__table)
|
|
42
56
|
sql.write(" SET ")
|
|
43
|
-
|
|
57
|
+
if self.__where_named_params:
|
|
58
|
+
sql.write(", ".join([f"{col} = :{col}" for col in self.__sets]))
|
|
59
|
+
else:
|
|
60
|
+
sql.write(", ".join([f"{col} = ?" for col in self.__sets]))
|
|
44
61
|
if self.__wheres:
|
|
45
62
|
sql.write(" WHERE ")
|
|
46
63
|
sql.write(" AND ".join(self.__wheres))
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
-
from sqlqb import
|
|
3
|
+
from sqlqb import sqlite3
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
@pytest.fixture
|
|
7
7
|
def conn():
|
|
8
|
-
c =
|
|
8
|
+
c = sqlite3.connect(":memory:")
|
|
9
9
|
c.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
|
|
10
10
|
c.executemany("INSERT INTO users VALUES (?, ?, ?)", [(1, "Alice", 30), (2, "Bob", 17), (3, "Carol", 25)])
|
|
11
11
|
c.commit()
|
|
@@ -101,7 +101,7 @@ class TestDelete:
|
|
|
101
101
|
|
|
102
102
|
def test_delete_with_limit_not_supported(self, conn):
|
|
103
103
|
# NOTE: sqlite library of python does not support LIMIT in delete queries
|
|
104
|
-
import sqlite3
|
|
104
|
+
import sqlite3 as stdlib_sqlite3
|
|
105
105
|
|
|
106
|
-
with pytest.raises(
|
|
106
|
+
with pytest.raises(stdlib_sqlite3.OperationalError):
|
|
107
107
|
conn.Delete().From("users").Limit(2).execute()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|