py.lib
2022-07-17
Parent:1668cc57225b
py.lib/db/migrator.py
. Используем в логах monotonic вместо time
| awgur@16 | 1 # coding: utf-8 |
| awgur@16 | 2 """\ |
| awgur@16 | 3 Инструмент миграции схемы БД. |
| awgur@16 | 4 |
| awgur@16 | 5 DOC: https://ws.a0fs.net/w/ncc.ws/devel/python/modules/db_migrator/ |
| awgur@16 | 6 """ |
| awgur@16 | 7 |
| awgur@16 | 8 from os.path import exists, join as p_join, isdir |
| awgur@16 | 9 from os import listdir |
| awgur@16 | 10 |
| awgur@16 | 11 |
| awgur@23 | 12 class MigrateError(Exception): |
| awgur@23 | 13 pass |
| awgur@16 | 14 |
| awgur@16 | 15 |
| awgur@16 | 16 class MigrateManager(object): |
| awgur@16 | 17 def __init__(self, control_table: str, migrate_env: str): |
| awgur@16 | 18 self.control_table = control_table |
| awgur@16 | 19 |
| awgur@16 | 20 if not exists(migrate_env): |
| awgur@16 | 21 raise MigrateError('Migrate enviroment not found') |
| awgur@16 | 22 |
| awgur@16 | 23 self.schema = p_join(migrate_env, 'schema.sql') |
| awgur@16 | 24 if not exists(self.schema): |
| awgur@23 | 25 raise MigrateError(f'Schema file not found: {self.schema}') |
| awgur@16 | 26 |
| awgur@16 | 27 self.patch_dir = p_join(migrate_env, 'patch') |
| awgur@16 | 28 if not isdir(self.patch_dir): |
| awgur@23 | 29 raise MigrateError(f'Patch dir not found or not directory: {self.patch_dir}') |
| awgur@16 | 30 |
| awgur@16 | 31 def get_patch_files(self, ver: int): |
| awgur@16 | 32 res = {} |
| awgur@16 | 33 for f in listdir(self.patch_dir): |
| awgur@16 | 34 if not f.lower().endswith('.sql'): |
| awgur@16 | 35 continue |
| awgur@16 | 36 |
| awgur@16 | 37 _f = f.strip().split('.') |
| awgur@16 | 38 |
| awgur@16 | 39 try: |
| awgur@16 | 40 _ver = int(_f[0]) |
| awgur@16 | 41 |
| awgur@16 | 42 except (TypeError, ValueError) as e: |
| awgur@23 | 43 raise MigrateError(f'Error on parse version "{_f[0]}" of file "{f}": {e}') |
| awgur@16 | 44 |
| awgur@16 | 45 except IndexError: |
| awgur@23 | 46 raise MigrateError(f'Error on get version from filename: {f}') |
| awgur@16 | 47 |
| awgur@16 | 48 if _ver in res: |
| awgur@23 | 49 raise MigrateError(f'Version duplicates on parse file: {f}') |
| awgur@16 | 50 |
| awgur@16 | 51 res[_ver] = p_join(self.patch_dir, f) |
| awgur@16 | 52 |
| awgur@16 | 53 for i in sorted(res.keys()): |
| awgur@16 | 54 if i > ver: |
| awgur@16 | 55 yield i, res[i] |
| awgur@16 | 56 |
| awgur@16 | 57 @staticmethod |
| awgur@16 | 58 def get_commands(file): |
| awgur@16 | 59 buf = [] |
| awgur@16 | 60 with open(file) as IN: |
| awgur@16 | 61 for l in IN: |
| awgur@16 | 62 if l.lstrip().startswith('--'): |
| awgur@16 | 63 if buf: |
| awgur@16 | 64 yield '\n'.join(buf) |
| awgur@16 | 65 buf[:] = [] |
| awgur@16 | 66 |
| awgur@16 | 67 else: |
| awgur@16 | 68 buf.append(l) |
| awgur@16 | 69 |
| awgur@16 | 70 if buf: |
| awgur@16 | 71 yield '\n'.join(buf) |
| awgur@16 | 72 |
| awgur@16 | 73 def init_db(self, db): |
| awgur@16 | 74 cursor = db.cursor() |
| awgur@16 | 75 for c in self.get_commands(self.schema): |
| awgur@19 | 76 cursor.execute(c) |
| awgur@19 | 77 db.commit() |
| awgur@16 | 78 |
| awgur@16 | 79 db.commit() |
| awgur@16 | 80 |
| awgur@16 | 81 def check(self, db): |
| awgur@16 | 82 cursor = db.cursor() |
| awgur@23 | 83 cursor.execute(f"SELECT version FROM {self.control_table}") |
| awgur@16 | 84 q = cursor.fetchone() |
| awgur@16 | 85 del cursor |
| awgur@16 | 86 |
| awgur@16 | 87 if q is None: |
| awgur@16 | 88 ver = -1 |
| awgur@16 | 89 else: |
| awgur@16 | 90 ver = int(q[0]) |
| awgur@16 | 91 |
| awgur@16 | 92 new_ver = ver |
| awgur@16 | 93 cursor = db.cursor() |
| awgur@16 | 94 for up_ver, patch_file in self.get_patch_files(ver): |
| awgur@16 | 95 new_ver = up_ver |
| awgur@16 | 96 for cmd in self.get_commands(patch_file): |
| awgur@16 | 97 cursor.execute(cmd) |
| awgur@16 | 98 db.commit() |
| awgur@16 | 99 |
| awgur@23 | 100 cursor.execute(f"DELETE FROM {self.control_table}") |
| awgur@17 | 101 |
| awgur@23 | 102 cursor.execute(f""" |
| awgur@23 | 103 INSERT INTO {self.control_table} (version) |
| awgur@23 | 104 VALUES ({new_ver}) |
| awgur@23 | 105 """) |
| awgur@17 | 106 db.commit() |
| awgur@16 | 107 |
| awgur@19 | 108 @staticmethod |
| awgur@19 | 109 def get_conn_from_my_obj(obj: object): |
| awgur@19 | 110 """\ |
| awgur@19 | 111 Получиение объекта соединения из обёрток, которые я сам себе пишу для работы с DB-API |
| awgur@16 | 112 |
| awgur@19 | 113 :param obj: |
| awgur@19 | 114 :return: |
| awgur@19 | 115 """ |
| awgur@19 | 116 if hasattr(obj, '_conn'): |
| awgur@19 | 117 return obj._conn |
| awgur@19 | 118 elif hasattr(obj, '_con'): |
| awgur@19 | 119 return obj._con |
| awgur@19 | 120 else: |
| awgur@19 | 121 raise TypeError('No known connection object in given database object found') |