Superset配置数据库连接串密码不能包含@

如果Superset元数据库连接配置SQLALCHEMY_DATABASE_URI中密码部分包含@符,则在执行superset db upgrade时异常,即使对@做转义也不能解决问题。例如,元数据配置参数SQLALCHEMY_DATABASE_URI如下:

1
SQLALCHEMY_DATABASE_URI = 'mysql://superset1:superset%4001@localhost/superset1?unix_socket=/tmp/mysql.sock&charset=utf8'

异常信息如下:

1
2
3
4
  File "/home/zhangjc/.pyenv/versions/3.11.9/envs/superset/lib/python3.11/site-packages/MySQLdb/connections.py", line 195, in __init__
super().__init__(*args, **kwargs2)
sqlalchemy.exc.OperationalError: (MySQLdb.OperationalError) (2005, "Unknown MySQL server host '01@localhost' (-2)")
(Background on this error at: https://sqlalche.me/e/14/e3q8)

通过以上信息可以看出,所以进行了转义,但仍然将密码部分的@符作为密码和主机的分隔符。

该问题目前唯一的解决方法是修改元数据库访问密码,不要包含@符。这是因为Superset Server与数据库迁移组件Alembic之间对连接串的处理存在冲突。

Superset Server会将连接串转换为sqlalchemy.engine.url.URL进行处理,所以不会导致解析错误,参考代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def get_uri(self):
if self._bind is None:
return self._app.config['SQLALCHEMY_DATABASE_URI']
binds = self._app.config.get('SQLALCHEMY_BINDS') or ()
assert self._bind in binds, \
'Bind %r is not specified. Set it in the SQLALCHEMY_BINDS ' \
'configuration variable' % self._bind
return binds[self._bind]

def get_engine(self):
with self._lock:
uri = self.get_uri()
echo = self._app.config['SQLALCHEMY_ECHO']
if (uri, echo) == self._connected_for:
return self._engine

sa_url = make_url(uri)
sa_url, options = self.get_options(sa_url, echo)
self._engine = rv = self._sa.create_engine(sa_url, options)

if _record_queries(self._app):
_EngineDebuggingSignalEvents(self._engine,
self._app.import_name).register()

self._connected_for = (uri, echo)

return rv

Alembic使用Python内置配置文件解析器configparser处理配置信息,处理时只能用字符串,对SQLALCHEMY_DATABASE_URI处理前会先解码,所以导致解析不正确。参考代码如下:

1
2
3
4
5
6
7
8
DATABASE_URI = current_app.config["SQLALCHEMY_DATABASE_URI"]
if "sqlite" in DATABASE_URI:
logger.warning(
"SQLite Database support for metadata databases will \
be removed in a future version of Superset."
)
decoded_uri = urllib.parse.unquote(DATABASE_URI)
config.set_main_option("sqlalchemy.url", decoded_uri)

虽然Alembic中可以用%%对%进行转义,但这样做会导致Superset Server解析配置异常。

可参考的链接如下: