|
7 | 7 | from typing import Optional, Sequence, Dict, Iterator, Callable, Set, Generator, Iterable, Tuple, Any |
8 | 8 | from typing import TypeVar, List |
9 | 9 |
|
10 | | -from sqlalchemy import select, delete, desc, func, Select, JSON, text, or_, not_, and_, exists, Row |
| 10 | +from sqlalchemy import select, delete, desc, func, Select, JSON, text, or_, not_, and_, exists, Row, update |
11 | 11 | from sqlalchemy.orm import Session |
12 | 12 | from typing_extensions import overload, Union, TypedDict, Unpack, NotRequired |
13 | 13 |
|
@@ -74,6 +74,11 @@ def __supports_json_query(cls) -> bool: |
74 | 74 | def __supports_vacuum_into(cls) -> bool: |
75 | 75 | return cls.__check_support(db_utils.check_sqlite_vacuum_into_support, 'SQLite backend does not support VACUUM INTO statement. Insecure manual file copy is used as the fallback') |
76 | 76 |
|
| 77 | + @classmethod |
| 78 | + @functools.lru_cache |
| 79 | + def __supports_row_number(cls) -> bool: |
| 80 | + return cls.__check_support(db_utils.check_sqlite_row_number, 'SQLite backend does not support ROW_NUMBER() statement, ID reassignment is not available') |
| 81 | + |
77 | 82 | @classmethod |
78 | 83 | def __validate_int_fields_range(cls, obj: schema.Base): |
79 | 84 | from sqlalchemy import Integer, BigInteger |
@@ -1588,3 +1593,44 @@ def iterate_backup_batch(self, *, batch_size: int = 1000) -> Iterator[List[schem |
1588 | 1593 |
|
1589 | 1594 | def delete_backup(self, backup: schema.Backup): |
1590 | 1595 | self.session.delete(backup) |
| 1596 | + |
| 1597 | + def reassign_backup_id(self, order: BackupSortOrder) -> Optional[int]: |
| 1598 | + if not self.__supports_row_number(): |
| 1599 | + raise RuntimeError('Current SQLite version {} does not support ROW_NUMBER() function'.format(db_utils.get_sqlite_version())) |
| 1600 | + |
| 1601 | + order_by: list |
| 1602 | + if order == BackupSortOrder.time: |
| 1603 | + order_by = [schema.Backup.timestamp, schema.Backup.timestamp_ns_part, schema.Backup.id] |
| 1604 | + elif order == BackupSortOrder.time_r: |
| 1605 | + order_by = [desc(schema.Backup.timestamp), desc(schema.Backup.timestamp_ns_part), desc(schema.Backup.id)] |
| 1606 | + elif order == BackupSortOrder.id: |
| 1607 | + order_by = [schema.Backup.id] |
| 1608 | + elif order == BackupSortOrder.id_r: |
| 1609 | + order_by = [desc(schema.Backup.id)] |
| 1610 | + else: |
| 1611 | + raise ValueError(order) |
| 1612 | + |
| 1613 | + numbered_cte = select( |
| 1614 | + schema.Backup.id, |
| 1615 | + func.row_number().over(order_by=order_by).label('new_id') |
| 1616 | + ).cte('numbered') |
| 1617 | + numbered_alias = numbered_cte.alias() |
| 1618 | + self.session.execute( |
| 1619 | + update(schema.Backup). |
| 1620 | + values(id=numbered_alias.c.new_id). |
| 1621 | + where(schema.Backup.id == numbered_alias.c.id) |
| 1622 | + ) |
| 1623 | + |
| 1624 | + max_id: Optional[int] = self.session.execute(select(func.max(schema.Backup.id))).scalar() |
| 1625 | + table_name = schema.Backup.__tablename__ |
| 1626 | + if max_id is not None: |
| 1627 | + self.session.execute( |
| 1628 | + text(f'''UPDATE sqlite_sequence SET seq = :seq WHERE name = :name'''). |
| 1629 | + bindparams(seq=max_id, name=table_name) |
| 1630 | + ) |
| 1631 | + else: |
| 1632 | + self.session.execute( |
| 1633 | + text(f'''DELETE FROM sqlite_sequence WHERE name = :name'''). |
| 1634 | + bindparams(name=table_name) |
| 1635 | + ) |
| 1636 | + return max_id |
0 commit comments