|
1 | 1 | """Database module.""" |
| 2 | +import argparse |
| 3 | +from pathlib import Path |
2 | 4 | from typing import List, Optional |
3 | 5 |
|
4 | | -from clit.files import shell |
| 6 | +from clit.constants import POSTGRES_DOCKER_CONTAINER_NAME |
| 7 | +from clit.docker import DockerContainer |
| 8 | +from clit.files import existing_directory_type, existing_file_type, shell |
5 | 9 |
|
6 | 10 |
|
7 | 11 | class DatabaseServer: |
@@ -80,3 +84,63 @@ def list_databases(self) -> "PostgreSQLServer": |
80 | 84 |
|
81 | 85 | self.databases = sorted(db.strip() for db in process.stdout.strip().split()) |
82 | 86 | return self |
| 87 | + |
| 88 | + |
| 89 | +def backup(parser, args): |
| 90 | + """Backup PostgreSQL databases.""" |
| 91 | + pg = PostgreSQLServer(args.server_uri).list_databases() |
| 92 | + container = DockerContainer(POSTGRES_DOCKER_CONTAINER_NAME) |
| 93 | + for database in pg.databases: |
| 94 | + sql_file: Path = Path(args.backup_dir) / f"{pg.protocol}_{pg.server}_{pg.port}" / f"{database}.sql" |
| 95 | + sql_file.parent.mkdir(parents=True, exist_ok=True) |
| 96 | + |
| 97 | + if pg.inside_docker: |
| 98 | + sql_file = container.replace_mount_dir(sql_file) |
| 99 | + shell(f"{pg.pg_dump} --clean --create --if-exists --file={sql_file} {pg.docker_uri}/{database}") |
| 100 | + |
| 101 | + |
| 102 | +def restore(parser, args): |
| 103 | + """Restore PostgreSQL databases.""" |
| 104 | + pg = PostgreSQLServer(args.server_uri).list_databases() |
| 105 | + new_database = args.database_name or args.sql_file.stem |
| 106 | + if new_database in pg.databases: |
| 107 | + print(f"The database {new_database!r} already exists in the server. Provide a new database name.") |
| 108 | + exit(1) |
| 109 | + |
| 110 | + if new_database != args.sql_file.stem: |
| 111 | + # TODO Optional argument --owner to set the database owner |
| 112 | + print(f"TODO: Create a user named {new_database!r} if it doesn't exist (or raise an error)") |
| 113 | + print(f"TODO: Parse the .sql file and replace DATABASE/OWNER {args.sql_file.stem!r} by {new_database!r}") |
| 114 | + exit(2) |
| 115 | + |
| 116 | + shell(f"{pg.psql} {args.server_uri} < {args.sql_file}") |
| 117 | + |
| 118 | + |
| 119 | +# TODO: Convert to click |
| 120 | +def extra_postgres(): |
| 121 | + """Extra PostgreSQL tools like backup, restore, user creation, etc.""" |
| 122 | + parser = argparse.ArgumentParser(description="PostgreSQL helper tools") |
| 123 | + parser.add_argument("server_uri", help="database server URI (postgresql://user:password@server:port)") |
| 124 | + parser.set_defaults(chosen_function=None) |
| 125 | + subparsers = parser.add_subparsers(title="commands") |
| 126 | + |
| 127 | + parser_backup = subparsers.add_parser("backup", help="backup a PostgreSQL database to a SQL file") |
| 128 | + parser_backup.add_argument("backup_dir", type=existing_directory_type, help="directory to store the backups") |
| 129 | + parser_backup.set_defaults(chosen_function=backup) |
| 130 | + |
| 131 | + parser_restore = subparsers.add_parser("restore", help="restore a PostgreSQL database from a SQL file") |
| 132 | + parser_restore.add_argument( |
| 133 | + "sql_file", type=existing_file_type, help="full path of the .sql file created by the 'backup' command" |
| 134 | + ) |
| 135 | + parser_restore.add_argument("database_name", nargs="?", help="database name (default: basename of .sql file)") |
| 136 | + parser_restore.set_defaults(chosen_function=restore) |
| 137 | + |
| 138 | + # TODO Subcommand create-user new-user-name or alias user new-user-name to create a new user |
| 139 | + # TODO xpostgres user myuser [mypass] |
| 140 | + |
| 141 | + args = parser.parse_args() |
| 142 | + if not args.chosen_function: |
| 143 | + parser.print_help() |
| 144 | + return |
| 145 | + args.chosen_function(parser, args) |
| 146 | + return |
0 commit comments