py.lib

Yohn Y. 2020-12-25 Child:10227cc154fa

16:6c38121a0c86 Go to Latest

py.lib/db/migrator.py

+ Добавления инструмента миграций

History
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@16 79 cursor.execute(c)
awgur@16 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@16 103 cursor.execute("""
awgur@16 104 UPDATE %s
awgur@16 105 SET version = %s
awgur@16 106 """ % (self.control_table, new_ver))
awgur@16 107
awgur@16 108