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