database-wrapper-pgsql 0.1.28__tar.gz → 0.1.33__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.
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/PKG-INFO +7 -7
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/README.md +5 -5
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql/__init__.py +8 -3
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql/connector.py +2 -2
- database_wrapper_pgsql-0.1.33/database_wrapper_pgsql/db_wrapper_pgsql.py +498 -0
- database_wrapper_pgsql-0.1.28/database_wrapper_pgsql/db_wrapper_pgsql.py → database_wrapper_pgsql-0.1.33/database_wrapper_pgsql/db_wrapper_pgsql_async.py +80 -118
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql.egg-info/PKG-INFO +7 -7
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql.egg-info/SOURCES.txt +1 -0
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql.egg-info/requires.txt +1 -1
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/pyproject.toml +2 -2
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql/py.typed +0 -0
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql.egg-info/dependency_links.txt +0 -0
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql.egg-info/top_level.txt +0 -0
- {database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: database_wrapper_pgsql
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.33
|
|
4
4
|
Summary: database_wrapper for PostgreSQL database
|
|
5
5
|
Author-email: Gints Murans <gm@gm.lv>
|
|
6
6
|
License: GNU General Public License v3.0 (GPL-3.0)
|
|
@@ -32,7 +32,7 @@ Classifier: Topic :: Software Development
|
|
|
32
32
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
33
33
|
Requires-Python: >=3.8
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
35
|
-
Requires-Dist: database_wrapper==0.1.
|
|
35
|
+
Requires-Dist: database_wrapper==0.1.33
|
|
36
36
|
Requires-Dist: psycopg[binary]>=3.2.0
|
|
37
37
|
Requires-Dist: psycopg[pool]>=3.2.0
|
|
38
38
|
|
|
@@ -40,7 +40,7 @@ Requires-Dist: psycopg[pool]>=3.2.0
|
|
|
40
40
|
|
|
41
41
|
_Part of the `database_wrapper` package._
|
|
42
42
|
|
|
43
|
-
This python package is a database wrapper for [PostgreSQL](https://www.postgresql.org/) (also called pgsql)
|
|
43
|
+
This python package is a database wrapper for [PostgreSQL](https://www.postgresql.org/) (also called pgsql) database.
|
|
44
44
|
|
|
45
45
|
## Installation
|
|
46
46
|
|
|
@@ -51,9 +51,9 @@ pip install database_wrapper[pgsql]
|
|
|
51
51
|
## Usage
|
|
52
52
|
|
|
53
53
|
```python
|
|
54
|
-
from database_wrapper_pgsql import
|
|
54
|
+
from database_wrapper_pgsql import PgSQLWithPoolingAsync, DBWrapperPgSQLAsync
|
|
55
55
|
|
|
56
|
-
db =
|
|
56
|
+
db = PgSQLWithPoolingAsync({
|
|
57
57
|
"hostname": "localhost",
|
|
58
58
|
"port": 3306,
|
|
59
59
|
"username": "root",
|
|
@@ -61,7 +61,7 @@ db = MySQL({
|
|
|
61
61
|
"database": "my_database"
|
|
62
62
|
})
|
|
63
63
|
db.open()
|
|
64
|
-
dbWrapper =
|
|
64
|
+
dbWrapper = DBWrapperPgSQLAsync(db=db)
|
|
65
65
|
|
|
66
66
|
# Simple query
|
|
67
67
|
aModel = MyModel()
|
|
@@ -78,7 +78,7 @@ else:
|
|
|
78
78
|
# Raw query
|
|
79
79
|
res = await dbWrapper.getAll(
|
|
80
80
|
aModel,
|
|
81
|
-
"""
|
|
81
|
+
customQuery="""
|
|
82
82
|
SELECT t1.*, t2.name AS other_name
|
|
83
83
|
FROM my_table AS t1
|
|
84
84
|
LEFT JOIN other_table AS t2 ON t1.other_id = t2.id
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
_Part of the `database_wrapper` package._
|
|
4
4
|
|
|
5
|
-
This python package is a database wrapper for [PostgreSQL](https://www.postgresql.org/) (also called pgsql)
|
|
5
|
+
This python package is a database wrapper for [PostgreSQL](https://www.postgresql.org/) (also called pgsql) database.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -13,9 +13,9 @@ pip install database_wrapper[pgsql]
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
15
|
```python
|
|
16
|
-
from database_wrapper_pgsql import
|
|
16
|
+
from database_wrapper_pgsql import PgSQLWithPoolingAsync, DBWrapperPgSQLAsync
|
|
17
17
|
|
|
18
|
-
db =
|
|
18
|
+
db = PgSQLWithPoolingAsync({
|
|
19
19
|
"hostname": "localhost",
|
|
20
20
|
"port": 3306,
|
|
21
21
|
"username": "root",
|
|
@@ -23,7 +23,7 @@ db = MySQL({
|
|
|
23
23
|
"database": "my_database"
|
|
24
24
|
})
|
|
25
25
|
db.open()
|
|
26
|
-
dbWrapper =
|
|
26
|
+
dbWrapper = DBWrapperPgSQLAsync(db=db)
|
|
27
27
|
|
|
28
28
|
# Simple query
|
|
29
29
|
aModel = MyModel()
|
|
@@ -40,7 +40,7 @@ else:
|
|
|
40
40
|
# Raw query
|
|
41
41
|
res = await dbWrapper.getAll(
|
|
42
42
|
aModel,
|
|
43
|
-
"""
|
|
43
|
+
customQuery="""
|
|
44
44
|
SELECT t1.*, t2.name AS other_name
|
|
45
45
|
FROM my_table AS t1
|
|
46
46
|
LEFT JOIN other_table AS t2 ON t1.other_id = t2.id
|
{database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql/__init__.py
RENAMED
|
@@ -9,7 +9,8 @@ Part of the database_wrapper package
|
|
|
9
9
|
import logging
|
|
10
10
|
|
|
11
11
|
from .db_wrapper_pgsql import DBWrapperPgSQL
|
|
12
|
-
from .
|
|
12
|
+
from .db_wrapper_pgsql_async import DBWrapperPgSQLAsync
|
|
13
|
+
from .connector import PgConfig, PgSQL, PgSQLWithPoolingAsync
|
|
13
14
|
|
|
14
15
|
# Set the logger to a quiet default, can be enabled if needed
|
|
15
16
|
logger = logging.getLogger("database_wrapper_pgsql")
|
|
@@ -18,8 +19,12 @@ if logger.level == logging.NOTSET:
|
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
__all__ = [
|
|
22
|
+
# Wrappers
|
|
21
23
|
"DBWrapperPgSQL",
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
+
"DBWrapperPgSQLAsync",
|
|
25
|
+
# Connectors
|
|
24
26
|
"PgSQL",
|
|
27
|
+
"PgSQLWithPoolingAsync",
|
|
28
|
+
# Helpers
|
|
29
|
+
"PgConfig",
|
|
25
30
|
]
|
{database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql/connector.py
RENAMED
|
@@ -115,7 +115,7 @@ class PgSQL(DatabaseBackend):
|
|
|
115
115
|
self.connection.rollback()
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
class
|
|
118
|
+
class PgSQLWithPoolingAsync(DatabaseBackend):
|
|
119
119
|
"""
|
|
120
120
|
PostgreSQL database implementation with async and connection pooling
|
|
121
121
|
|
|
@@ -223,7 +223,7 @@ class AsyncPgSQLWithPooling(DatabaseBackend):
|
|
|
223
223
|
# Lets do some socket magic
|
|
224
224
|
self.fixSocketTimeouts(connection.fileno())
|
|
225
225
|
|
|
226
|
-
async with timer.aenter("
|
|
226
|
+
async with timer.aenter("PgSQLWithPoolingAsync.__aenter__.ping"):
|
|
227
227
|
async with connection.transaction():
|
|
228
228
|
await cursor.execute("SELECT 1")
|
|
229
229
|
await cursor.fetchone()
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Generator, overload
|
|
3
|
+
|
|
4
|
+
from psycopg import Cursor, sql
|
|
5
|
+
from psycopg.rows import class_row
|
|
6
|
+
|
|
7
|
+
from database_wrapper import T, OrderByItem, DBWrapper, DBDataModel
|
|
8
|
+
|
|
9
|
+
from .connector import (
|
|
10
|
+
# Sync
|
|
11
|
+
PgConnectionType,
|
|
12
|
+
PgCursorType,
|
|
13
|
+
PgSQL,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DBWrapperPgSQL(DBWrapper):
|
|
18
|
+
"""
|
|
19
|
+
Sync database wrapper for postgres
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
# Override db instance
|
|
23
|
+
db: PgSQL
|
|
24
|
+
""" PostgreSQL database connector """
|
|
25
|
+
|
|
26
|
+
dbConn: PgConnectionType | None = None
|
|
27
|
+
""" PostgreSQL connection object """
|
|
28
|
+
|
|
29
|
+
#######################
|
|
30
|
+
### Class lifecycle ###
|
|
31
|
+
#######################
|
|
32
|
+
|
|
33
|
+
# Meta methods
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
db: PgSQL,
|
|
37
|
+
dbConn: PgConnectionType | None = None,
|
|
38
|
+
logger: logging.Logger | None = None,
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Initializes a new instance of the DBWrapper class.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
db (MySQL): The PostgreSQL connector.
|
|
45
|
+
dbConn (MySqlConnection, optional): The PostgreSQL connection object. Defaults to None.
|
|
46
|
+
logger (logging.Logger, optional): The logger object. Defaults to None.
|
|
47
|
+
"""
|
|
48
|
+
super().__init__(db, dbConn, logger)
|
|
49
|
+
|
|
50
|
+
######################
|
|
51
|
+
### Helper methods ###
|
|
52
|
+
######################
|
|
53
|
+
|
|
54
|
+
def makeIdentifier(self, schema: str | None, name: str) -> sql.Identifier | str:
|
|
55
|
+
"""
|
|
56
|
+
Creates a SQL identifier object from the given name.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
name (str): The name to create the identifier from.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
sql.Identifier: The created SQL identifier object.
|
|
63
|
+
"""
|
|
64
|
+
if schema:
|
|
65
|
+
return sql.Identifier(schema, name)
|
|
66
|
+
|
|
67
|
+
return sql.Identifier(name)
|
|
68
|
+
|
|
69
|
+
@overload
|
|
70
|
+
def createCursor(self) -> PgCursorType: ...
|
|
71
|
+
|
|
72
|
+
@overload
|
|
73
|
+
def createCursor(
|
|
74
|
+
self,
|
|
75
|
+
emptyDataClass: T,
|
|
76
|
+
) -> Cursor[T]: ...
|
|
77
|
+
|
|
78
|
+
def createCursor(
|
|
79
|
+
self,
|
|
80
|
+
emptyDataClass: T | None = None,
|
|
81
|
+
) -> Cursor[T] | PgCursorType:
|
|
82
|
+
"""
|
|
83
|
+
Creates a new cursor object.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
emptyDataClass (DBDataModel | None, optional): The data model to use for the cursor.
|
|
87
|
+
Defaults to None.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
PgAsyncCursorType | AsyncCursor[DBDataModel]: The created cursor object.
|
|
91
|
+
"""
|
|
92
|
+
assert self.db is not None, "Database connection is not set"
|
|
93
|
+
|
|
94
|
+
# First we need connection
|
|
95
|
+
if self.dbConn is None:
|
|
96
|
+
self.dbConn = self.db.connection
|
|
97
|
+
|
|
98
|
+
# Lets make sure we have a connection
|
|
99
|
+
if self.dbConn is None:
|
|
100
|
+
raise Exception("Failed to get connection")
|
|
101
|
+
|
|
102
|
+
if emptyDataClass is None:
|
|
103
|
+
return self.dbConn.cursor()
|
|
104
|
+
|
|
105
|
+
return self.dbConn.cursor(row_factory=class_row(emptyDataClass.__class__))
|
|
106
|
+
|
|
107
|
+
def logQuery(
|
|
108
|
+
self,
|
|
109
|
+
cursor: Cursor[Any],
|
|
110
|
+
query: sql.SQL | sql.Composed,
|
|
111
|
+
params: tuple[Any, ...],
|
|
112
|
+
) -> None:
|
|
113
|
+
"""
|
|
114
|
+
Logs the given query and parameters.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
cursor (Any): The database cursor.
|
|
118
|
+
query (Any): The query to log.
|
|
119
|
+
params (tuple[Any, ...]): The parameters to log.
|
|
120
|
+
"""
|
|
121
|
+
queryString = query.as_string(self.dbConn)
|
|
122
|
+
self.logger.debug(f"Query: {queryString}")
|
|
123
|
+
|
|
124
|
+
#####################
|
|
125
|
+
### Query methods ###
|
|
126
|
+
#####################
|
|
127
|
+
|
|
128
|
+
def filterQuery(
|
|
129
|
+
self,
|
|
130
|
+
schemaName: str | None,
|
|
131
|
+
tableName: str,
|
|
132
|
+
) -> sql.SQL | sql.Composed | str:
|
|
133
|
+
"""
|
|
134
|
+
Creates a SQL query to filter data from the given table.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
schemaName (str): The name of the schema to filter data from.
|
|
138
|
+
tableName (str): The name of the table to filter data from.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
sql.SQL | sql.Composed: The created SQL query object.
|
|
142
|
+
"""
|
|
143
|
+
return sql.SQL("SELECT * FROM {table}").format(
|
|
144
|
+
table=self.makeIdentifier(schemaName, tableName)
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
def limitQuery(self, offset: int = 0, limit: int = 100) -> sql.Composed | sql.SQL:
|
|
148
|
+
return sql.SQL("LIMIT {} OFFSET {}").format(limit, offset)
|
|
149
|
+
|
|
150
|
+
# Action methods
|
|
151
|
+
def getOne(
|
|
152
|
+
self,
|
|
153
|
+
emptyDataClass: T,
|
|
154
|
+
customQuery: sql.SQL | sql.Composed | str | None = None,
|
|
155
|
+
) -> T | None:
|
|
156
|
+
"""
|
|
157
|
+
Retrieves a single record from the database.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
emptyDataClass (T): The data model to use for the query.
|
|
161
|
+
customQuery (sql.SQL | sql.Composed | str | None, optional): The custom query to use.
|
|
162
|
+
Defaults to None.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
T | None: The result of the query.
|
|
166
|
+
"""
|
|
167
|
+
# Query
|
|
168
|
+
_query = (
|
|
169
|
+
customQuery
|
|
170
|
+
or emptyDataClass.queryBase()
|
|
171
|
+
or self.filterQuery(emptyDataClass.schemaName, emptyDataClass.tableName)
|
|
172
|
+
)
|
|
173
|
+
idKey = emptyDataClass.idKey
|
|
174
|
+
idValue = emptyDataClass.id
|
|
175
|
+
if not idKey:
|
|
176
|
+
raise ValueError("Id key is not set")
|
|
177
|
+
if not idValue:
|
|
178
|
+
raise ValueError("Id value is not set")
|
|
179
|
+
|
|
180
|
+
# Create a SQL object for the query and format it
|
|
181
|
+
querySql = sql.SQL("{query} WHERE {idkey} = %s").format(
|
|
182
|
+
query=_query, idkey=self.makeIdentifier(emptyDataClass.tableAlias, idKey)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Create a new cursor
|
|
186
|
+
newCursor = self.createCursor(emptyDataClass)
|
|
187
|
+
|
|
188
|
+
# Log
|
|
189
|
+
self.logQuery(newCursor, querySql, (idValue,))
|
|
190
|
+
|
|
191
|
+
# Load data
|
|
192
|
+
try:
|
|
193
|
+
|
|
194
|
+
newCursor.execute(querySql, (idValue,))
|
|
195
|
+
dbData = newCursor.fetchone()
|
|
196
|
+
|
|
197
|
+
return dbData
|
|
198
|
+
|
|
199
|
+
finally:
|
|
200
|
+
# Close the cursor
|
|
201
|
+
newCursor.close()
|
|
202
|
+
|
|
203
|
+
def getByKey(
|
|
204
|
+
self,
|
|
205
|
+
emptyDataClass: T,
|
|
206
|
+
idKey: str,
|
|
207
|
+
idValue: Any,
|
|
208
|
+
customQuery: sql.SQL | sql.Composed | str | None = None,
|
|
209
|
+
) -> T | None:
|
|
210
|
+
"""
|
|
211
|
+
Retrieves a single record from the database using the given key.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
emptyDataClass (T): The data model to use for the query.
|
|
215
|
+
idKey (str): The name of the key to use for the query.
|
|
216
|
+
idValue (Any): The value of the key to use for the query.
|
|
217
|
+
customQuery (sql.SQL | sql.Composed | str | None, optional): The custom query to use.
|
|
218
|
+
Defaults to None.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
T | None: The result of the query.
|
|
222
|
+
"""
|
|
223
|
+
# Query
|
|
224
|
+
_query = (
|
|
225
|
+
customQuery
|
|
226
|
+
or emptyDataClass.queryBase()
|
|
227
|
+
or self.filterQuery(emptyDataClass.schemaName, emptyDataClass.tableName)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Create a SQL object for the query and format it
|
|
231
|
+
querySql = sql.SQL("{} WHERE {} = %s").format(
|
|
232
|
+
_query, self.makeIdentifier(emptyDataClass.tableAlias, idKey)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Create a new cursor
|
|
236
|
+
newCursor = self.createCursor(emptyDataClass)
|
|
237
|
+
|
|
238
|
+
# Log
|
|
239
|
+
self.logQuery(newCursor, querySql, (idValue,))
|
|
240
|
+
|
|
241
|
+
# Load data
|
|
242
|
+
try:
|
|
243
|
+
newCursor.execute(querySql, (idValue,))
|
|
244
|
+
dbData = newCursor.fetchone()
|
|
245
|
+
|
|
246
|
+
return dbData
|
|
247
|
+
|
|
248
|
+
finally:
|
|
249
|
+
# Ensure the cursor is closed after the generator is exhausted or an error occurs
|
|
250
|
+
newCursor.close()
|
|
251
|
+
|
|
252
|
+
def getAll(
|
|
253
|
+
self,
|
|
254
|
+
emptyDataClass: T,
|
|
255
|
+
idKey: str | None = None,
|
|
256
|
+
idValue: Any | None = None,
|
|
257
|
+
orderBy: OrderByItem | None = None,
|
|
258
|
+
offset: int = 0,
|
|
259
|
+
limit: int = 100,
|
|
260
|
+
customQuery: sql.SQL | sql.Composed | str | None = None,
|
|
261
|
+
) -> Generator[T, None, None]:
|
|
262
|
+
"""
|
|
263
|
+
Retrieves all records from the database.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
emptyDataClass (T): The data model to use for the query.
|
|
267
|
+
idKey (str | None, optional): The name of the key to use for filtering. Defaults to None.
|
|
268
|
+
idValue (Any | None, optional): The value of the key to use for filtering. Defaults to None.
|
|
269
|
+
orderBy (OrderByItem | None, optional): The order by item to use for sorting. Defaults to None.
|
|
270
|
+
offset (int, optional): The number of results to skip. Defaults to 0.
|
|
271
|
+
limit (int, optional): The maximum number of results to return. Defaults to 100.
|
|
272
|
+
customQuery (sql.SQL | sql.Composed | str | None, optional): The custom query to use. Defaults to None.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Generator[T, None, None]: The result of the query.
|
|
276
|
+
"""
|
|
277
|
+
# Query
|
|
278
|
+
_query = (
|
|
279
|
+
customQuery
|
|
280
|
+
or emptyDataClass.queryBase()
|
|
281
|
+
or self.filterQuery(emptyDataClass.schemaName, emptyDataClass.tableName)
|
|
282
|
+
)
|
|
283
|
+
_params: tuple[Any, ...] = ()
|
|
284
|
+
|
|
285
|
+
# Filter
|
|
286
|
+
if idKey and idValue:
|
|
287
|
+
_query = sql.SQL("{} WHERE {} = %s").format(
|
|
288
|
+
_query, self.makeIdentifier(emptyDataClass.tableAlias, idKey)
|
|
289
|
+
)
|
|
290
|
+
_params = (idValue,)
|
|
291
|
+
|
|
292
|
+
# Limits
|
|
293
|
+
_order: sql.Composable = sql.SQL("")
|
|
294
|
+
_limit: sql.Composable = sql.SQL("")
|
|
295
|
+
|
|
296
|
+
if orderBy:
|
|
297
|
+
orderList = [
|
|
298
|
+
f"{item[0]} {item[1] if len(item) > 1 and item[1] != None else 'ASC'}"
|
|
299
|
+
for item in orderBy
|
|
300
|
+
]
|
|
301
|
+
_order = sql.SQL("ORDER BY %s" % ", ".join(orderList)) # type: ignore
|
|
302
|
+
if offset or limit:
|
|
303
|
+
_limit = sql.SQL("{}").format(self.limitQuery(offset, limit))
|
|
304
|
+
|
|
305
|
+
# Create a SQL object for the query and format it
|
|
306
|
+
querySql = sql.SQL("{query} {order} {limit}").format(
|
|
307
|
+
query=_query, order=_order, limit=_limit
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# Create a new cursor
|
|
311
|
+
newCursor = self.createCursor(emptyDataClass)
|
|
312
|
+
|
|
313
|
+
# Log
|
|
314
|
+
self.logQuery(newCursor, querySql, _params)
|
|
315
|
+
|
|
316
|
+
# Load data
|
|
317
|
+
try:
|
|
318
|
+
newCursor.execute(querySql, _params)
|
|
319
|
+
while True:
|
|
320
|
+
row = newCursor.fetchone()
|
|
321
|
+
if row is None:
|
|
322
|
+
break
|
|
323
|
+
yield row
|
|
324
|
+
|
|
325
|
+
finally:
|
|
326
|
+
# Close the cursor
|
|
327
|
+
newCursor.close()
|
|
328
|
+
|
|
329
|
+
def getFiltered(
|
|
330
|
+
self,
|
|
331
|
+
emptyDataClass: T,
|
|
332
|
+
filter: dict[str, Any],
|
|
333
|
+
orderBy: OrderByItem | None = None,
|
|
334
|
+
offset: int = 0,
|
|
335
|
+
limit: int = 100,
|
|
336
|
+
customQuery: sql.SQL | sql.Composed | str | None = None,
|
|
337
|
+
) -> Generator[T, None, None]:
|
|
338
|
+
# Filter
|
|
339
|
+
_query = (
|
|
340
|
+
customQuery
|
|
341
|
+
or emptyDataClass.queryBase()
|
|
342
|
+
or self.filterQuery(emptyDataClass.schemaName, emptyDataClass.tableName)
|
|
343
|
+
)
|
|
344
|
+
(_filter, _params) = self.createFilter(filter)
|
|
345
|
+
_filter = sql.SQL(_filter) # type: ignore
|
|
346
|
+
|
|
347
|
+
# Limits
|
|
348
|
+
_order: sql.Composable = sql.SQL("")
|
|
349
|
+
_limit: sql.Composable = sql.SQL("")
|
|
350
|
+
|
|
351
|
+
if orderBy:
|
|
352
|
+
orderList = [
|
|
353
|
+
f"{item[0]} {item[1] if len(item) > 1 and item[1] != None else 'ASC'}"
|
|
354
|
+
for item in orderBy
|
|
355
|
+
]
|
|
356
|
+
_order = sql.SQL("ORDER BY %s" % ", ".join(orderList)) # type: ignore
|
|
357
|
+
if offset or limit:
|
|
358
|
+
_limit = sql.SQL("{}").format(self.limitQuery(offset, limit))
|
|
359
|
+
|
|
360
|
+
# Create a SQL object for the query and format it
|
|
361
|
+
querySql = sql.SQL("{query} {filter} {order} {limit}").format(
|
|
362
|
+
query=_query, filter=_filter, order=_order, limit=_limit
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Create a new cursor
|
|
366
|
+
newCursor = self.createCursor(emptyDataClass)
|
|
367
|
+
|
|
368
|
+
# Log
|
|
369
|
+
self.logQuery(newCursor, querySql, _params)
|
|
370
|
+
|
|
371
|
+
# Load data
|
|
372
|
+
try:
|
|
373
|
+
newCursor.execute(querySql, _params)
|
|
374
|
+
while True:
|
|
375
|
+
row = newCursor.fetchone()
|
|
376
|
+
if row is None:
|
|
377
|
+
break
|
|
378
|
+
yield row
|
|
379
|
+
|
|
380
|
+
finally:
|
|
381
|
+
# Close the cursor
|
|
382
|
+
newCursor.close()
|
|
383
|
+
|
|
384
|
+
def _store(
|
|
385
|
+
self,
|
|
386
|
+
emptyDataClass: DBDataModel,
|
|
387
|
+
schemaName: str | None,
|
|
388
|
+
tableName: str,
|
|
389
|
+
storeData: dict[str, Any],
|
|
390
|
+
idKey: str,
|
|
391
|
+
) -> tuple[int, int]:
|
|
392
|
+
keys = storeData.keys()
|
|
393
|
+
values = list(storeData.values())
|
|
394
|
+
|
|
395
|
+
tableIdentifier = self.makeIdentifier(schemaName, tableName)
|
|
396
|
+
returnKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
|
|
397
|
+
|
|
398
|
+
insertQuery = sql.SQL(
|
|
399
|
+
"INSERT INTO {table} ({columns}) VALUES ({values}) RETURNING {id_key}"
|
|
400
|
+
).format(
|
|
401
|
+
table=tableIdentifier,
|
|
402
|
+
columns=sql.SQL(", ").join(map(sql.Identifier, keys)),
|
|
403
|
+
values=sql.SQL(", ").join(sql.Placeholder() * len(values)),
|
|
404
|
+
id_key=returnKey,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
# Create a new cursor
|
|
408
|
+
newCursor = self.createCursor(emptyDataClass)
|
|
409
|
+
|
|
410
|
+
# Log
|
|
411
|
+
self.logQuery(newCursor, insertQuery, tuple(values))
|
|
412
|
+
|
|
413
|
+
# Insert
|
|
414
|
+
try:
|
|
415
|
+
newCursor.execute(insertQuery, tuple(values))
|
|
416
|
+
affectedRows = newCursor.rowcount
|
|
417
|
+
result = newCursor.fetchone()
|
|
418
|
+
|
|
419
|
+
return (
|
|
420
|
+
result.id if result and hasattr(result, "id") else 0,
|
|
421
|
+
affectedRows,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
finally:
|
|
425
|
+
# Close the cursor
|
|
426
|
+
newCursor.close()
|
|
427
|
+
|
|
428
|
+
def _update(
|
|
429
|
+
self,
|
|
430
|
+
emptyDataClass: DBDataModel,
|
|
431
|
+
schemaName: str | None,
|
|
432
|
+
tableName: str,
|
|
433
|
+
updateData: dict[str, Any],
|
|
434
|
+
updateId: tuple[str, Any],
|
|
435
|
+
) -> int:
|
|
436
|
+
(idKey, idValue) = updateId
|
|
437
|
+
keys = updateData.keys()
|
|
438
|
+
values = list(updateData.values())
|
|
439
|
+
values.append(idValue)
|
|
440
|
+
|
|
441
|
+
set_clause = sql.SQL(", ").join(
|
|
442
|
+
sql.Identifier(key) + sql.SQL(" = %s") for key in keys
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
tableIdentifier = self.makeIdentifier(schemaName, tableName)
|
|
446
|
+
updateKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
|
|
447
|
+
updateQuery = sql.SQL(
|
|
448
|
+
"UPDATE {table} SET {set_clause} WHERE {id_key} = %s"
|
|
449
|
+
).format(
|
|
450
|
+
table=tableIdentifier,
|
|
451
|
+
set_clause=set_clause,
|
|
452
|
+
id_key=updateKey,
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# Create a new cursor
|
|
456
|
+
newCursor = self.createCursor(emptyDataClass)
|
|
457
|
+
|
|
458
|
+
# Log
|
|
459
|
+
self.logQuery(newCursor, updateQuery, tuple(values))
|
|
460
|
+
|
|
461
|
+
# Update
|
|
462
|
+
try:
|
|
463
|
+
newCursor.execute(updateQuery, tuple(values))
|
|
464
|
+
affectedRows = newCursor.rowcount
|
|
465
|
+
|
|
466
|
+
return affectedRows
|
|
467
|
+
|
|
468
|
+
finally:
|
|
469
|
+
# Close the cursor
|
|
470
|
+
newCursor.close()
|
|
471
|
+
|
|
472
|
+
def _delete(
|
|
473
|
+
self,
|
|
474
|
+
emptyDataClass: DBDataModel,
|
|
475
|
+
schemaName: str | None,
|
|
476
|
+
tableName: str,
|
|
477
|
+
deleteId: tuple[str, Any],
|
|
478
|
+
) -> int:
|
|
479
|
+
(idKey, idValue) = deleteId
|
|
480
|
+
|
|
481
|
+
tableIdentifier = self.makeIdentifier(schemaName, tableName)
|
|
482
|
+
deleteKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
|
|
483
|
+
|
|
484
|
+
delete_query = sql.SQL("DELETE FROM {table} WHERE {id_key} = %s").format(
|
|
485
|
+
table=tableIdentifier, id_key=deleteKey
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
# Create a new cursor
|
|
489
|
+
newCursor = self.createCursor(emptyDataClass)
|
|
490
|
+
|
|
491
|
+
# Log
|
|
492
|
+
self.logQuery(newCursor, delete_query, (idValue,))
|
|
493
|
+
|
|
494
|
+
# Delete
|
|
495
|
+
newCursor.execute(delete_query, (idValue,))
|
|
496
|
+
affected_rows = newCursor.rowcount
|
|
497
|
+
|
|
498
|
+
return affected_rows
|
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from typing import Any, AsyncGenerator, overload
|
|
3
3
|
|
|
4
|
-
from psycopg import
|
|
4
|
+
from psycopg import AsyncCursor, sql
|
|
5
5
|
from psycopg.rows import class_row
|
|
6
6
|
|
|
7
|
-
from database_wrapper import T, OrderByItem,
|
|
7
|
+
from database_wrapper import T, OrderByItem, DBWrapperAsync, DBDataModel
|
|
8
8
|
|
|
9
9
|
from .connector import (
|
|
10
|
-
# Sync
|
|
11
|
-
PgConnectionType,
|
|
12
|
-
PgCursorType,
|
|
13
|
-
PgSQL,
|
|
14
10
|
# Async
|
|
15
11
|
PgAsyncConnectionType,
|
|
16
12
|
PgAsyncCursorType,
|
|
17
|
-
|
|
13
|
+
PgSQLWithPoolingAsync,
|
|
18
14
|
)
|
|
19
15
|
|
|
20
16
|
|
|
21
|
-
class
|
|
17
|
+
class DBWrapperPgSQLAsync(DBWrapperAsync):
|
|
22
18
|
"""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
This is meant to be used in async environments. Also remember to call close() when done.
|
|
19
|
+
Async database wrapper for postgres
|
|
26
20
|
|
|
21
|
+
This is meant to be used in async environments.
|
|
22
|
+
Also remember to call close() when done as we cannot do that in __del__.
|
|
27
23
|
"""
|
|
28
24
|
|
|
29
25
|
# Override db instance
|
|
30
|
-
db:
|
|
31
|
-
|
|
26
|
+
db: PgSQLWithPoolingAsync
|
|
27
|
+
""" Async PostgreSQL database connector """
|
|
28
|
+
|
|
29
|
+
dbConn: PgAsyncConnectionType | None = None
|
|
30
|
+
""" Async PostgreSQL connection object """
|
|
32
31
|
|
|
33
32
|
#######################
|
|
34
33
|
### Class lifecycle ###
|
|
@@ -37,24 +36,25 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
37
36
|
# Meta methods
|
|
38
37
|
def __init__(
|
|
39
38
|
self,
|
|
40
|
-
db:
|
|
41
|
-
dbConn:
|
|
39
|
+
db: PgSQLWithPoolingAsync,
|
|
40
|
+
dbConn: PgAsyncConnectionType | None = None,
|
|
42
41
|
logger: logging.Logger | None = None,
|
|
43
42
|
):
|
|
44
43
|
"""
|
|
45
44
|
Initializes a new instance of the DBWrapper class.
|
|
46
45
|
|
|
47
46
|
Args:
|
|
48
|
-
db (MySQL): The
|
|
47
|
+
db (MySQL): The PostgreSQL database connector.
|
|
48
|
+
dbConn (MySqlConnection, optional): The PostgreSQL connection object. Defaults to None.
|
|
49
49
|
logger (logging.Logger, optional): The logger object. Defaults to None.
|
|
50
50
|
"""
|
|
51
51
|
super().__init__(db, dbConn, logger)
|
|
52
52
|
|
|
53
53
|
async def close(self) -> None:
|
|
54
54
|
if hasattr(self, "dbConn") and self.dbConn and hasattr(self, "db") and self.db:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
await self.db.returnConnection(self.dbConn)
|
|
56
|
+
|
|
57
|
+
await super().close()
|
|
58
58
|
|
|
59
59
|
######################
|
|
60
60
|
### Helper methods ###
|
|
@@ -76,18 +76,18 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
76
76
|
return sql.Identifier(name)
|
|
77
77
|
|
|
78
78
|
@overload
|
|
79
|
-
async def createCursor(self) ->
|
|
79
|
+
async def createCursor(self) -> PgAsyncCursorType: ...
|
|
80
80
|
|
|
81
81
|
@overload
|
|
82
82
|
async def createCursor(
|
|
83
83
|
self,
|
|
84
84
|
emptyDataClass: T,
|
|
85
|
-
) ->
|
|
85
|
+
) -> AsyncCursor[T]: ...
|
|
86
86
|
|
|
87
87
|
async def createCursor(
|
|
88
88
|
self,
|
|
89
89
|
emptyDataClass: T | None = None,
|
|
90
|
-
) ->
|
|
90
|
+
) -> AsyncCursor[T] | PgAsyncCursorType:
|
|
91
91
|
"""
|
|
92
92
|
Creates a new cursor object.
|
|
93
93
|
|
|
@@ -102,20 +102,12 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
102
102
|
|
|
103
103
|
# First we need connection
|
|
104
104
|
if self.dbConn is None:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if isinstance(self.db, AsyncPgSQLWithPooling):
|
|
109
|
-
status = await self.db.newConnection()
|
|
110
|
-
if not status:
|
|
111
|
-
raise Exception("Failed to create new connection")
|
|
105
|
+
status = await self.db.newConnection()
|
|
106
|
+
if not status:
|
|
107
|
+
raise Exception("Failed to create new connection")
|
|
112
108
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
# Lets make sure we have a connection
|
|
117
|
-
if self.dbConn is None:
|
|
118
|
-
raise Exception("Failed to get connection")
|
|
109
|
+
(pgConn, _pgCur) = status
|
|
110
|
+
self.dbConn = pgConn
|
|
119
111
|
|
|
120
112
|
if emptyDataClass is None:
|
|
121
113
|
return self.dbConn.cursor()
|
|
@@ -124,7 +116,7 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
124
116
|
|
|
125
117
|
def logQuery(
|
|
126
118
|
self,
|
|
127
|
-
cursor: AsyncCursor[Any]
|
|
119
|
+
cursor: AsyncCursor[Any],
|
|
128
120
|
query: sql.SQL | sql.Composed,
|
|
129
121
|
params: tuple[Any, ...],
|
|
130
122
|
) -> None:
|
|
@@ -208,21 +200,14 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
208
200
|
|
|
209
201
|
# Load data
|
|
210
202
|
try:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
dbData = await newCursor.fetchone()
|
|
214
|
-
else:
|
|
215
|
-
newCursor.execute(querySql, (idValue,))
|
|
216
|
-
dbData = newCursor.fetchone()
|
|
203
|
+
await newCursor.execute(querySql, (idValue,))
|
|
204
|
+
dbData = await newCursor.fetchone()
|
|
217
205
|
|
|
218
206
|
return dbData
|
|
219
207
|
|
|
220
208
|
finally:
|
|
221
209
|
# Close the cursor
|
|
222
|
-
|
|
223
|
-
await newCursor.close()
|
|
224
|
-
else:
|
|
225
|
-
newCursor.close()
|
|
210
|
+
await newCursor.close()
|
|
226
211
|
|
|
227
212
|
async def getByKey(
|
|
228
213
|
self,
|
|
@@ -264,21 +249,14 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
264
249
|
|
|
265
250
|
# Load data
|
|
266
251
|
try:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
dbData = await newCursor.fetchone()
|
|
270
|
-
else:
|
|
271
|
-
newCursor.execute(querySql, (idValue,))
|
|
272
|
-
dbData = newCursor.fetchone()
|
|
252
|
+
await newCursor.execute(querySql, (idValue,))
|
|
253
|
+
dbData = await newCursor.fetchone()
|
|
273
254
|
|
|
274
255
|
return dbData
|
|
275
256
|
|
|
276
257
|
finally:
|
|
277
258
|
# Ensure the cursor is closed after the generator is exhausted or an error occurs
|
|
278
|
-
|
|
279
|
-
await newCursor.close()
|
|
280
|
-
else:
|
|
281
|
-
newCursor.close()
|
|
259
|
+
await newCursor.close()
|
|
282
260
|
|
|
283
261
|
async def getAll(
|
|
284
262
|
self,
|
|
@@ -346,29 +324,19 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
346
324
|
|
|
347
325
|
# Load data
|
|
348
326
|
try:
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
else:
|
|
360
|
-
newCursor.execute(querySql, _params)
|
|
361
|
-
while True:
|
|
362
|
-
row = newCursor.fetchone()
|
|
363
|
-
if row is None:
|
|
364
|
-
break
|
|
365
|
-
yield row
|
|
327
|
+
# Execute the query
|
|
328
|
+
await newCursor.execute(querySql, _params)
|
|
329
|
+
|
|
330
|
+
# Instead of fetchall(), we'll use a generator to yield results one by one
|
|
331
|
+
while True:
|
|
332
|
+
row = await newCursor.fetchone()
|
|
333
|
+
if row is None:
|
|
334
|
+
break
|
|
335
|
+
yield row
|
|
336
|
+
|
|
366
337
|
finally:
|
|
367
338
|
# Ensure the cursor is closed after the generator is exhausted or an error occurs
|
|
368
|
-
|
|
369
|
-
await newCursor.close()
|
|
370
|
-
else:
|
|
371
|
-
newCursor.close()
|
|
339
|
+
await newCursor.close()
|
|
372
340
|
|
|
373
341
|
async def getFiltered(
|
|
374
342
|
self,
|
|
@@ -414,29 +382,19 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
414
382
|
|
|
415
383
|
# Load data
|
|
416
384
|
try:
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
else:
|
|
428
|
-
newCursor.execute(querySql, _params)
|
|
429
|
-
while True:
|
|
430
|
-
row = newCursor.fetchone()
|
|
431
|
-
if row is None:
|
|
432
|
-
break
|
|
433
|
-
yield row
|
|
385
|
+
# Execute the query
|
|
386
|
+
await newCursor.execute(querySql, _params)
|
|
387
|
+
|
|
388
|
+
# Instead of fetchall(), we'll use a generator to yield results one by one
|
|
389
|
+
while True:
|
|
390
|
+
row = await newCursor.fetchone()
|
|
391
|
+
if row is None:
|
|
392
|
+
break
|
|
393
|
+
yield row
|
|
394
|
+
|
|
434
395
|
finally:
|
|
435
|
-
#
|
|
436
|
-
|
|
437
|
-
await newCursor.close()
|
|
438
|
-
else:
|
|
439
|
-
newCursor.close()
|
|
396
|
+
# Close the cursor
|
|
397
|
+
await newCursor.close()
|
|
440
398
|
|
|
441
399
|
async def _store(
|
|
442
400
|
self,
|
|
@@ -468,19 +426,19 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
468
426
|
self.logQuery(newCursor, insertQuery, tuple(values))
|
|
469
427
|
|
|
470
428
|
# Insert
|
|
471
|
-
|
|
429
|
+
try:
|
|
472
430
|
await newCursor.execute(insertQuery, tuple(values))
|
|
473
431
|
affectedRows = newCursor.rowcount
|
|
474
432
|
result = await newCursor.fetchone()
|
|
475
|
-
else:
|
|
476
|
-
newCursor.execute(insertQuery, tuple(values))
|
|
477
|
-
affectedRows = newCursor.rowcount
|
|
478
|
-
result = newCursor.fetchone()
|
|
479
433
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
434
|
+
return (
|
|
435
|
+
result.id if result and hasattr(result, "id") else 0,
|
|
436
|
+
affectedRows,
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
finally:
|
|
440
|
+
# Close the cursor
|
|
441
|
+
await newCursor.close()
|
|
484
442
|
|
|
485
443
|
async def _update(
|
|
486
444
|
self,
|
|
@@ -516,13 +474,15 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
516
474
|
self.logQuery(newCursor, updateQuery, tuple(values))
|
|
517
475
|
|
|
518
476
|
# Update
|
|
519
|
-
|
|
477
|
+
try:
|
|
520
478
|
await newCursor.execute(updateQuery, tuple(values))
|
|
521
|
-
|
|
522
|
-
newCursor.execute(updateQuery, tuple(values))
|
|
523
|
-
affectedRows = newCursor.rowcount
|
|
479
|
+
affectedRows = newCursor.rowcount
|
|
524
480
|
|
|
525
|
-
|
|
481
|
+
return affectedRows
|
|
482
|
+
|
|
483
|
+
finally:
|
|
484
|
+
# Close the cursor
|
|
485
|
+
await newCursor.close()
|
|
526
486
|
|
|
527
487
|
async def _delete(
|
|
528
488
|
self,
|
|
@@ -547,10 +507,12 @@ class DBWrapperPgSQL(DBWrapper):
|
|
|
547
507
|
self.logQuery(newCursor, delete_query, (idValue,))
|
|
548
508
|
|
|
549
509
|
# Delete
|
|
550
|
-
|
|
510
|
+
try:
|
|
551
511
|
await newCursor.execute(delete_query, (idValue,))
|
|
552
|
-
|
|
553
|
-
newCursor.execute(delete_query, (idValue,))
|
|
554
|
-
affected_rows = newCursor.rowcount
|
|
512
|
+
affected_rows = newCursor.rowcount
|
|
555
513
|
|
|
556
|
-
|
|
514
|
+
return affected_rows
|
|
515
|
+
|
|
516
|
+
finally:
|
|
517
|
+
# Close the cursor
|
|
518
|
+
await newCursor.close()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: database_wrapper_pgsql
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.33
|
|
4
4
|
Summary: database_wrapper for PostgreSQL database
|
|
5
5
|
Author-email: Gints Murans <gm@gm.lv>
|
|
6
6
|
License: GNU General Public License v3.0 (GPL-3.0)
|
|
@@ -32,7 +32,7 @@ Classifier: Topic :: Software Development
|
|
|
32
32
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
33
33
|
Requires-Python: >=3.8
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
35
|
-
Requires-Dist: database_wrapper==0.1.
|
|
35
|
+
Requires-Dist: database_wrapper==0.1.33
|
|
36
36
|
Requires-Dist: psycopg[binary]>=3.2.0
|
|
37
37
|
Requires-Dist: psycopg[pool]>=3.2.0
|
|
38
38
|
|
|
@@ -40,7 +40,7 @@ Requires-Dist: psycopg[pool]>=3.2.0
|
|
|
40
40
|
|
|
41
41
|
_Part of the `database_wrapper` package._
|
|
42
42
|
|
|
43
|
-
This python package is a database wrapper for [PostgreSQL](https://www.postgresql.org/) (also called pgsql)
|
|
43
|
+
This python package is a database wrapper for [PostgreSQL](https://www.postgresql.org/) (also called pgsql) database.
|
|
44
44
|
|
|
45
45
|
## Installation
|
|
46
46
|
|
|
@@ -51,9 +51,9 @@ pip install database_wrapper[pgsql]
|
|
|
51
51
|
## Usage
|
|
52
52
|
|
|
53
53
|
```python
|
|
54
|
-
from database_wrapper_pgsql import
|
|
54
|
+
from database_wrapper_pgsql import PgSQLWithPoolingAsync, DBWrapperPgSQLAsync
|
|
55
55
|
|
|
56
|
-
db =
|
|
56
|
+
db = PgSQLWithPoolingAsync({
|
|
57
57
|
"hostname": "localhost",
|
|
58
58
|
"port": 3306,
|
|
59
59
|
"username": "root",
|
|
@@ -61,7 +61,7 @@ db = MySQL({
|
|
|
61
61
|
"database": "my_database"
|
|
62
62
|
})
|
|
63
63
|
db.open()
|
|
64
|
-
dbWrapper =
|
|
64
|
+
dbWrapper = DBWrapperPgSQLAsync(db=db)
|
|
65
65
|
|
|
66
66
|
# Simple query
|
|
67
67
|
aModel = MyModel()
|
|
@@ -78,7 +78,7 @@ else:
|
|
|
78
78
|
# Raw query
|
|
79
79
|
res = await dbWrapper.getAll(
|
|
80
80
|
aModel,
|
|
81
|
-
"""
|
|
81
|
+
customQuery="""
|
|
82
82
|
SELECT t1.*, t2.name AS other_name
|
|
83
83
|
FROM my_table AS t1
|
|
84
84
|
LEFT JOIN other_table AS t2 ON t1.other_id = t2.id
|
|
@@ -3,6 +3,7 @@ pyproject.toml
|
|
|
3
3
|
database_wrapper_pgsql/__init__.py
|
|
4
4
|
database_wrapper_pgsql/connector.py
|
|
5
5
|
database_wrapper_pgsql/db_wrapper_pgsql.py
|
|
6
|
+
database_wrapper_pgsql/db_wrapper_pgsql_async.py
|
|
6
7
|
database_wrapper_pgsql/py.typed
|
|
7
8
|
database_wrapper_pgsql.egg-info/PKG-INFO
|
|
8
9
|
database_wrapper_pgsql.egg-info/SOURCES.txt
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "database_wrapper_pgsql"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.33"
|
|
8
8
|
description = "database_wrapper for PostgreSQL database"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -35,7 +35,7 @@ classifiers = [
|
|
|
35
35
|
]
|
|
36
36
|
keywords = ["database", "wrapper", "python", "postgresql", "pgsql"]
|
|
37
37
|
dependencies = [
|
|
38
|
-
"database_wrapper == 0.1.
|
|
38
|
+
"database_wrapper == 0.1.33",
|
|
39
39
|
"psycopg[binary] >= 3.2.0",
|
|
40
40
|
"psycopg[pool] >= 3.2.0",
|
|
41
41
|
]
|
{database_wrapper_pgsql-0.1.28 → database_wrapper_pgsql-0.1.33}/database_wrapper_pgsql/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|