Skip to content

Commit

Permalink
bump v0.4.0
Browse files Browse the repository at this point in the history
- remove the use of ".env"
- ref: project structure
- deps: remove "dotenv"
  • Loading branch information
Grvzard committed Nov 29, 2023
1 parent fc65d79 commit 23a7ce5
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 101 deletions.
2 changes: 1 addition & 1 deletion ibdx/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.3.3'
__version__ = '0.4.0'
43 changes: 13 additions & 30 deletions ibdx/cli.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,27 @@
import zipfile
import typer
import logging

from .ibd_backup import ibd_backup
from .ibd_restore import ibd_restore
from .tools import zipfile_ls
from .deps import complete_filename

from ibdx.ibd_backup import ibd_backup
from ibdx.ibd_restore import ibd_restore
from ibdx.deps import complete_filename

logging.basicConfig(level='INFO')
cli = typer.Typer()


@cli.command()
def backup(
dbname: str = typer.Option(..., '--db', '-d'),
tables_pattern: str = typer.Option('*', '--tables', '-t'),
output_filename: str = typer.Option(..., '--file', '-f', autocompletion=complete_filename),
datadir: str = typer.Option(''),
):
try:
ibd_backup(dbname, tables_pattern, output_filename, datadir)
except Exception as e:
typer.echo(f'ibdx error: {e}')


@cli.command()
def restore(
dbname: str = typer.Option(..., '--db', '-d'),
tables_pattern: str = typer.Option('*', '--tables', '-t'),
input_filename: str = typer.Option(..., '--file', '-f', autocompletion=complete_filename),
datadir: str = typer.Option(''),
):
try:
ibd_restore(dbname, tables_pattern, input_filename, datadir)
except Exception as e:
typer.echo(f'ibdx error: {e}')
cli.command('backup')(ibd_backup)
cli.command('restore')(ibd_restore)


@cli.command()
def ls(zipfile_name: str = typer.Argument('', autocompletion=complete_filename)):
try:
for name in zipfile_ls(zipfile_name):
if not zipfile.is_zipfile(zipfile_name):
raise Exception('zipfile_name is not a zip file')
zip_file = zipfile.ZipFile(zipfile_name, 'r', zipfile.ZIP_DEFLATED)

for name in zip_file.namelist():
typer.echo(name)
except Exception as e:
typer.echo(f'ibdx error: {e}')
12 changes: 0 additions & 12 deletions ibdx/configs.py

This file was deleted.

62 changes: 40 additions & 22 deletions ibdx/ibd_backup.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,66 @@
import zipfile
from pathlib import Path
import fnmatch
import typer
import logging

from . import __version__
from .mysql_db_quick import MysqlConn
from .configs import DB_CONFIG
from ibdx import __version__
from ibdx.deps import complete_filename
from ibdx.mysql_db_quick import MysqlConn

logger = logging.getLogger(__name__)


def ibd_backup(
dbname: str,
tables_pattern: str,
out_fpath: str,
datadir: str = '',
dbname: str = typer.Option(..., '--db', '-d'),
host: str = typer.Option('127.0.0.1', '--host', '-h'),
port: int = typer.Option(3306, '--port', '-p'),
user: str = typer.Option('root', '--user', '-u'),
password: str = typer.Option('', '--password', '-P'),
tables_pattern: str = typer.Option('*', '--tables', '-t'),
fout_path: str = typer.Option(..., '--file', '-f', autocompletion=complete_filename),
datadir: str = typer.Option(''),
) -> None:
db = MysqlConn(dbname, DB_CONFIG)
db = MysqlConn(dbname, host, port, user, password)

if not datadir:
res = db.query('show variables like \'datadir\';').fetchone()
res = db.query("show variables like 'datadir';").fetchone()
if res is None:
raise Exception('cannot get datadir')
logger.error('cannot get datadir')
return
datadir = res[1]
if not Path(datadir).exists():
raise Exception('datadir does not exists')
logger.error('datadir does not exists')
return

db_dpath = Path(datadir) / dbname
assert db_dpath.is_dir()

_out_fpath = Path(out_fpath)

if not _out_fpath.exists():
with zipfile.ZipFile(_out_fpath, 'w', zipfile.ZIP_DEFLATED) as zip_file:
zip_file.writestr(f'.ibdx.v{__version__}', f'{__version__}')
_out_fpath = Path(fout_path)

tables = fnmatch.filter(db.get_tables(), tables_pattern)
if not tables:
logger.error('no table macthes')
return

host_info = db.get_version()
if _out_fpath.exists():
logger.error('can not write to an exists archive')
return

with zipfile.ZipFile(_out_fpath, 'a', zipfile.ZIP_DEFLATED) as zip_file:
arc = zipfile.ZipFile(_out_fpath, 'w', zipfile.ZIP_DEFLATED)
try:
for table in tables:
print(f"backup table: {table}")
logger.info(f'backing up table: {table}')

db.query(f"flush tables `{table}` for export;")
db.query(f'flush tables `{table}` for export;')
try:
sql_create = db.query(f'show create table `{table}`;').fetchall()[0][1]
zip_file.writestr(f'{table}.sql', sql_create)
zip_file.write(db_dpath / f'{table}.ibd', f'{table}.ibd')
zip_file.write(db_dpath / f'{table}.cfg', f'{table}.cfg')
arc.writestr(f'{table}.sql', sql_create)
arc.write(db_dpath / f'{table}.ibd', f'{table}.ibd')
arc.write(db_dpath / f'{table}.cfg', f'{table}.cfg')
arc.writestr(f'{table}.ibdx', f'ibdx.v{__version__}\n{host_info}')
finally:
db.query('unlock tables;')
finally:
arc.close()
48 changes: 29 additions & 19 deletions ibdx/ibd_restore.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,42 @@
from pathlib import Path
from contextlib import suppress
import fnmatch
import typer
import logging

from .mysql_db_quick import MysqlConn
from .configs import DB_CONFIG
from ibdx.mysql_db_quick import MysqlConn
from ibdx.deps import complete_filename

logger = logging.getLogger(__name__)


def ibd_restore(
dbname: str,
tables_pattern: str,
in_fpath: str,
datadir: str,
dbname: str = typer.Option(..., '--db', '-d'),
host: str = typer.Option('127.0.0.1', '--host', '-h'),
port: int = typer.Option(3306, '--port', '-p'),
user: str = typer.Option('root', '--user', '-u'),
password: str = typer.Option('', '--password', '-P'),
tables_pattern: str = typer.Option('*', '--tables', '-t'),
fin_path: str = typer.Option(..., '--file', '-f', autocompletion=complete_filename),
datadir: str = typer.Option(''),
) -> None:
db = MysqlConn(dbname, DB_CONFIG)
db = MysqlConn(dbname, host, port, user, password)

if not zipfile.is_zipfile(in_fpath):
raise Exception('in_fpath is not a zip file')
if not zipfile.is_zipfile(fin_path):
logger.error('--file is not a valid archive file')
return

if not datadir:
res = db.query('show variables like \'datadir\';').fetchone()
res = db.query("show variables like 'datadir';").fetchone()
if res is None:
raise Exception('cannot get datadir')
logger.error('cannot get datadir')
return
datadir = res[1]

db_path = Path(datadir) / dbname
assert db_path.is_dir()

with zipfile.ZipFile(in_fpath, 'r', zipfile.ZIP_DEFLATED) as zip_file:
with zipfile.ZipFile(fin_path, 'r', zipfile.ZIP_DEFLATED) as zip_file:
target_ibd_files = fnmatch.filter(
zip_file.namelist(),
f'{tables_pattern}.ibd',
Expand All @@ -39,34 +49,34 @@ def ibd_restore(

for sql_file in target_sql_files:
table = sql_file.rsplit('.')[0]
print(f"executing sql: {table}")
logger.info(f'executing sql: {table}')

with suppress(Exception):
sql_create = zip_file.read(sql_file)
db.query(sql_create)

for ibd_file in target_ibd_files:
table = ibd_file.rsplit('.')[0]
print(f"importing tablespace: {table}")
logger.info(f'importing tablespace: {table}')

try:
db.query(f'alter table `{table}` discard tablespace')
print(f'. alter table `{table}` discard tablespace')
logger.info(f'. alter table `{table}` discard tablespace')

zip_file.extract(f'{table}.ibd', db_path)
(db_path / f'{table}.ibd').chmod(0o666)
print(f'.. extract {table}.ibd')
logger.info(f'.. extract {table}.ibd')
with suppress(Exception):
zip_file.extract(f'{table}.cfg', db_path)
print(f'.. extract {table}.cfg')
logger.info(f'.. extract {table}.cfg')

db.query(f'alter table `{table}` import tablespace')
print(f'... alter table `{table}` import tablespace')
logger.info(f'... alter table `{table}` import tablespace')

except Exception as e:
(db_path / f'{table}.ibd').unlink(missing_ok=True)
(db_path / f'{table}.cfg').unlink(missing_ok=True)

# db.query(f'drop table if exists `{table}`;')

raise Exception('failed when importing tablespace: ' + str(e))
logger.error('failed when importing tablespace: ' + str(e))
16 changes: 11 additions & 5 deletions ibdx/mysql_db_quick.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@


class MysqlConn:
def __init__(self, db, config):
self.config = config
self.conn = connect(**self.config, database=db)
def __init__(self, db: str, host='127.0.0.1', port=3306, user='root', password=''):
self.conn = connect(host=host, port=port, user=user, password=password, database=db)
self.db = db

def __enter__(self):
return self

def __exit__(self, exc_type, exc_val, exc_tb):
def __exit__(self, **exc_info):
self.conn.close()

def query(self, sql):
Expand All @@ -19,6 +18,13 @@ def query(self, sql):
self.conn.commit()
return cursor

def get_tables(self):
def get_tables(self) -> list[str]:
with self.query('show tables') as cur:
return [rs_tup[0] for rs_tup in cur]

def get_version(self) -> str:
with self.query('select version()') as cur:
if res := cur.fetchone():
return res[0]
else:
raise Exception("failed to read server's version.")
10 changes: 0 additions & 10 deletions ibdx/tools.py

This file was deleted.

3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ description = "yet another backup/restore program for mysql InnoDB tables"
readme = "README.md"
requires-python = ">=3.8"
dependencies = [
"pymysql ==1.1.0",
"pymysql == 1.1.0",
"typer >= 0.7.0, < 1.0.0",
"python-dotenv == 0.21.0",
]

[project.scripts]
Expand Down

0 comments on commit 23a7ce5

Please sign in to comment.