Coverage for .tox/coverage/lib/python3.11/site-packages/wuttamess/mysql.py: 100%
28 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-17 16:03 -0500
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-17 16:03 -0500
1# -*- coding: utf-8; -*-
2################################################################################
3#
4# WuttaMess -- Fabric Automation Helpers
5# Copyright © 2024-2026 Lance Edgar
6#
7# This file is part of Wutta Framework.
8#
9# Wutta Framework is free software: you can redistribute it and/or modify it
10# under the terms of the GNU General Public License as published by the Free
11# Software Foundation, either version 3 of the License, or (at your option) any
12# later version.
13#
14# Wutta Framework is distributed in the hope that it will be useful, but
15# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17# more details.
18#
19# You should have received a copy of the GNU General Public License along with
20# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
21#
22################################################################################
23"""
24MySQL DB utilities
25"""
28def sql(c, sql_, database="", **kwargs):
29 """
30 Execute some SQL.
32 :param c: Fabric connection.
34 :param sql_: SQL string to execute.
36 :param database: Name of the database on which to execute the SQL.
38 :returns: Result of the ``c.run()`` call (``mysql -e`` command).
39 """
40 # some crazy quoting required here, see also
41 # http://stackoverflow.com/a/1250279
42 sql_ = sql_.replace("'", "'\"'\"'")
44 return c.run(
45 f"mysql --execute='{sql_}' --batch --skip-column-names {database}", **kwargs
46 )
49def user_exists(c, name, host="localhost"):
50 """
51 Determine if a given MySQL user exists.
53 :param c: Fabric connection.
55 :param name: Username to check for.
57 :param host: Host portion for the MySQL user account; default is
58 ``localhost``.
60 :returns: ``True`` if user exists, else ``False``.
61 """
62 user = sql(
63 c,
64 f"SELECT User FROM user WHERE User = '{name}' and Host = '{host}'",
65 database="mysql",
66 ).stdout.strip()
67 return user == name
70def create_user(c, name, host="localhost", password=None, checkfirst=True):
71 """
72 Create a MySQL user account.
74 :param c: Fabric connection.
76 :param name: Username to create.
78 :param host: Host portion for the MySQL user account; default is
79 ``localhost``.
81 :param password: Optional password for the new user. If set, will
82 call :func:`set_user_password()`.
84 :param checkfirst: If true (the default), first call
85 :func:`user_exists()` and skip creating if already present. If
86 false, then try to create user with no check.
87 """
88 if not checkfirst or not user_exists(c, name, host):
89 sql(c, f"CREATE USER '{name}'@'{host}';")
90 if password:
91 set_user_password(c, name, password, host=host)
94def set_user_password(c, name, password, host="localhost"):
95 """
96 Set the password for a MySQL user account.
98 :param c: Fabric connection.
100 :param name: Username whose password is to be set.
102 :param password: New password for the user.
104 :param host: Host portion for the MySQL user account; default is
105 ``localhost``.
106 """
107 # supposedly this is the new way to do it..
108 result = sql(
109 c,
110 f"ALTER USER '{name}'@'{host}' IDENTIFIED BY '{password}';",
111 echo=False,
112 hide=True,
113 warn=True,
114 )
115 if result.failed: # ..but it may fail for older systems,
116 # in which case we try it the old way
117 sql(
118 c,
119 f"SET PASSWORD FOR '{name}'@'{host}' = PASSWORD('{password}');",
120 echo=False,
121 hide=True,
122 )
125def db_exists(c, name):
126 """
127 Determine if a given MySQL database exists.
129 :param c: Fabric connection.
131 :param name: Name of the database to check for.
133 :returns: ``True`` if database exists, else ``False``.
134 """
135 db = sql(
136 c,
137 f"SELECT SCHEMA_NAME FROM SCHEMATA WHERE SCHEMA_NAME = '{name}'",
138 database="information_schema",
139 ).stdout.strip()
140 return db == name
143def create_db(c, name, checkfirst=True, user=None):
144 """
145 Create a MySQL database.
147 :param c: Fabric connection.
149 :param name: Name of the database to create.
151 :param checkfirst: If true (the default), first call
152 :func:`db_exists()` and skip creating if already present. If
153 false, then try to create DB with no check.
155 :param user: Optional user which should be granted full access to
156 the database. If specified, should include username *and*
157 host, e.g. ``poser@localhost``. See also
158 :meth:`grant_access()`.
159 """
160 if not checkfirst or not db_exists(c, name):
161 c.run(f"mysqladmin create {name}")
162 if user:
163 grant_access(c, name, user)
166def grant_access(c, dbname, user):
167 """
168 Grant full access to the given database for the given user.
170 :param c: Fabric connection.
172 :param dbname: Name of the database to which access should be granted.
174 :param user: MySQL user which should be granted access. Must
175 specify username *and* host, e.g. ``poser@localhost``.
176 """
177 sql(c, f"grant all on `{dbname}`.* to {user}")
180def drop_db(c, name, checkfirst=True):
181 """
182 Drop a MySQL database.
184 :param c: Fabric connection.
186 :param name: Name of the database to drop.
188 :param checkfirst: If true (the default), first call
189 :func:`db_exists()` and skip dropping if not present. If
190 false, then try to drop DB with no check.
191 """
192 if not checkfirst or db_exists(c, name):
193 c.run(f"mysqladmin drop --force {name}")