Migrate to aiosqlite, add some undocumented features, will be documented later. Some problems with mass_actions, don't use them, because loading algorythm may change
This commit is contained in:
@@ -3,14 +3,15 @@ This module includes functions to insert multiple records
|
||||
to a bound database at one time, with one time open and closing
|
||||
of the database file.
|
||||
"""
|
||||
from typing import TypeVar, Union, List, Tuple
|
||||
import aiosqlite
|
||||
from dataclasses import asdict
|
||||
from typing import List, Tuple, TypeVar, Union
|
||||
from warnings import warn
|
||||
from .constraints import ConstraintFailedError
|
||||
from .commons import _convert_sql_format, _create_table
|
||||
import sqlite3 as sql
|
||||
|
||||
T = TypeVar('T')
|
||||
from .commons import _convert_sql_format
|
||||
from .constraints import ConstraintFailedError
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class HeterogeneousCollectionError(Exception):
|
||||
@@ -19,45 +20,56 @@ class HeterogeneousCollectionError(Exception):
|
||||
ie: If a List or Tuple has elements of multiple
|
||||
types.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def _check_homogeneity(objects: Union[List[T], Tuple[T]]) -> None:
|
||||
"""
|
||||
Check if all of the members a Tuple or a List
|
||||
Check if all the members a Tuple or a List
|
||||
is of the same type.
|
||||
|
||||
:param objects: Tuple or list to check.
|
||||
:return: If all of the members of the same type.
|
||||
:return: If all the members of the same type.
|
||||
"""
|
||||
class_ = objects[0].__class__
|
||||
if not all([isinstance(obj, class_) or isinstance(objects[0], obj.__class__) for obj in objects]):
|
||||
if not all(
|
||||
[
|
||||
isinstance(obj, class_) or isinstance(objects[0], obj.__class__)
|
||||
for obj in objects
|
||||
]
|
||||
):
|
||||
raise HeterogeneousCollectionError("Tuple or List is not homogeneous.")
|
||||
|
||||
|
||||
def _toggle_memory_protection(cur: sql.Cursor, protect_memory: bool) -> None:
|
||||
async def _toggle_memory_protection(cur: aiosqlite.Cursor, protect_memory: bool) -> None:
|
||||
"""
|
||||
Given a cursor to an sqlite3 connection, if memory protection is false,
|
||||
Given a cursor to a sqlite3 connection, if memory protection is false,
|
||||
toggle memory protections off.
|
||||
|
||||
:param cur: Cursor to an open SQLite3 connection.
|
||||
:param protect_memory: Whether or not should memory be protected.
|
||||
:param protect_memory: Whether should memory be protected.
|
||||
:return: Memory protections off.
|
||||
"""
|
||||
if not protect_memory:
|
||||
warn("Memory protections are turned off, "
|
||||
"if operations are interrupted, file may get corrupt.", RuntimeWarning)
|
||||
cur.execute("PRAGMA synchronous = OFF")
|
||||
cur.execute("PRAGMA journal_mode = MEMORY")
|
||||
warn(
|
||||
"Memory protections are turned off, "
|
||||
"if operations are interrupted, file may get corrupt.",
|
||||
RuntimeWarning,
|
||||
)
|
||||
await cur.execute("PRAGMA synchronous = OFF")
|
||||
await cur.execute("PRAGMA journal_mode = MEMORY")
|
||||
|
||||
|
||||
def _mass_insert(objects: Union[List[T], Tuple[T]], db_name: str, protect_memory: bool = True) -> None:
|
||||
async def _mass_insert(
|
||||
objects: Union[List[T], Tuple[T]], db_name: str, protect_memory: bool = True
|
||||
) -> None:
|
||||
"""
|
||||
Insert multiple records into an SQLite3 database.
|
||||
|
||||
:param objects: Objects to insert.
|
||||
:param db_name: Name of the database to insert.
|
||||
:param protect_memory: Whether or not memory
|
||||
:param protect_memory: Whether memory
|
||||
protections are on or off.
|
||||
:return: None
|
||||
"""
|
||||
@@ -69,24 +81,28 @@ def _mass_insert(objects: Union[List[T], Tuple[T]], db_name: str, protect_memory
|
||||
for i, obj in enumerate(objects):
|
||||
kv_pairs = asdict(obj).items()
|
||||
setattr(obj, "obj_id", first_index + i + 1)
|
||||
sql_queries.append(f"INSERT INTO {table_name}(" +
|
||||
f"{', '.join(item[0] for item in kv_pairs)})" +
|
||||
f" VALUES ({', '.join(_convert_sql_format(item[1]) for item in kv_pairs)});")
|
||||
with sql.connect(db_name) as con:
|
||||
cur: sql.Cursor = con.cursor()
|
||||
sql_queries.append(
|
||||
f"INSERT INTO {table_name}("
|
||||
+ f"{', '.join(item[0] for item in kv_pairs)})"
|
||||
+ f" VALUES ({', '.join(_convert_sql_format(item[1], getattr(obj, 'types_table')) for item in kv_pairs)});"
|
||||
)
|
||||
async with aiosqlite.connect(db_name) as con:
|
||||
cur: aiosqlite.Cursor = await con.cursor()
|
||||
try:
|
||||
_toggle_memory_protection(cur, protect_memory)
|
||||
cur.execute(f"SELECT obj_id FROM {table_name} ORDER BY obj_id DESC LIMIT 1")
|
||||
index_tuple = cur.fetchone()
|
||||
await _toggle_memory_protection(cur, protect_memory)
|
||||
await cur.execute(f"SELECT obj_id FROM {table_name} ORDER BY obj_id DESC LIMIT 1")
|
||||
index_tuple = await cur.fetchone()
|
||||
if index_tuple:
|
||||
first_index = index_tuple[0]
|
||||
cur.executescript("BEGIN TRANSACTION;\n" + '\n'.join(sql_queries) + '\nEND TRANSACTION;')
|
||||
except sql.IntegrityError:
|
||||
_ = index_tuple[0]
|
||||
await cur.executescript(
|
||||
"BEGIN TRANSACTION;\n" + "\n".join(sql_queries) + "\nEND TRANSACTION;"
|
||||
)
|
||||
except aiosqlite.IntegrityError:
|
||||
raise ConstraintFailedError
|
||||
con.commit()
|
||||
await con.commit()
|
||||
|
||||
|
||||
def create_many(objects: Union[List[T], Tuple[T]], protect_memory: bool = True) -> None:
|
||||
async def create_many(objects: Union[List[T], Tuple[T]], protect_memory: bool = True) -> None:
|
||||
"""
|
||||
Insert many records corresponding to objects
|
||||
in a tuple or a list.
|
||||
@@ -98,29 +114,34 @@ def create_many(objects: Union[List[T], Tuple[T]], protect_memory: bool = True)
|
||||
:return: None.
|
||||
"""
|
||||
if objects:
|
||||
_mass_insert(objects, getattr(objects[0], "db_path"), protect_memory)
|
||||
await _mass_insert(objects, getattr(objects[0], "db_path"), protect_memory)
|
||||
else:
|
||||
raise ValueError("Collection is empty.")
|
||||
|
||||
|
||||
def copy_many(objects: Union[List[T], Tuple[T]], db_name: str, protect_memory: bool = True) -> None:
|
||||
async def copy_many(
|
||||
objects: Union[List[T], Tuple[T]], db_name: str, protect_memory: bool = True
|
||||
) -> None:
|
||||
"""
|
||||
Copy many records to another database, from
|
||||
their original database to new database, do
|
||||
their original database to a new database, do
|
||||
not delete old records.
|
||||
|
||||
:param objects: Objects to copy.
|
||||
:param db_name: Name of the new database.
|
||||
:param protect_memory: Wheter to protect memory during operation,
|
||||
:param protect_memory: Whether to protect memory during operation,
|
||||
Setting this to False will quicken the operation, but if the
|
||||
operation is cut short, database file will corrupt.
|
||||
operation is cut short, the database file will corrupt.
|
||||
:return: None
|
||||
"""
|
||||
if objects:
|
||||
with sql.connect(db_name) as con:
|
||||
cur = con.cursor()
|
||||
_create_table(objects[0].__class__, cur)
|
||||
con.commit()
|
||||
_mass_insert(objects, db_name, protect_memory)
|
||||
async with aiosqlite.connect(db_name) as con:
|
||||
cur = await con.cursor()
|
||||
await objects[0].markup_table(class_=objects[0].__class__, cursor=cur)
|
||||
await con.commit()
|
||||
await _mass_insert(objects, db_name, protect_memory)
|
||||
else:
|
||||
raise ValueError("Collection is empty.")
|
||||
|
||||
|
||||
__all__ = ['copy_many', 'create_many', 'HeterogeneousCollectionError']
|
||||
|
||||
Reference in New Issue
Block a user