diff --git a/pyproject.toml b/pyproject.toml index cfd2f444516..92efcf0a99f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ dependencies = [ "cryptography>=42.0.4, <45.0.0", "deprecation>=2.1.0, <2.2.0", "flask>=2.2.5, <3.0.0", - "flask-appbuilder>=5.0.0,<6", + "flask-appbuilder>=5.0.2,<6", "flask-caching>=2.1.0, <3", "flask-compress>=1.13, <2.0", "flask-talisman>=1.0.0, <2.0", diff --git a/requirements/base.txt b/requirements/base.txt index db48526bb70..076404726a4 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -116,7 +116,7 @@ flask==2.3.3 # flask-session # flask-sqlalchemy # flask-wtf -flask-appbuilder==5.0.0 +flask-appbuilder==5.0.2 # via # apache-superset (pyproject.toml) # apache-superset-core diff --git a/requirements/development.txt b/requirements/development.txt index 3a2877cf577..b479423133e 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -249,7 +249,7 @@ flask==2.3.3 # flask-sqlalchemy # flask-testing # flask-wtf -flask-appbuilder==5.0.0 +flask-appbuilder==5.0.2 # via # -c requirements/base-constraint.txt # apache-superset diff --git a/superset-core/pyproject.toml b/superset-core/pyproject.toml index 0511509caa1..88b21657bd8 100644 --- a/superset-core/pyproject.toml +++ b/superset-core/pyproject.toml @@ -42,7 +42,7 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ - "flask-appbuilder>=5.0.0,<6", + "flask-appbuilder>=5.0.2,<6", ] [project.urls] diff --git a/superset/migrations/shared/utils.py b/superset/migrations/shared/utils.py index 9adf7978bfe..dbcfa2f9f5b 100644 --- a/superset/migrations/shared/utils.py +++ b/superset/migrations/shared/utils.py @@ -57,13 +57,13 @@ DEFAULT_BATCH_SIZE = int(os.environ.get("BATCH_SIZE", 1000)) def get_table_column( table_name: str, column_name: str, -) -> Optional[list[dict[str, Any]]]: +) -> Optional[dict[str, Any]]: """ Get the specified column. :param table_name: The Table name :param column_name: The column name - :returns: The column + :returns: The column dictionary or None if not found """ insp = inspect(op.get_context().bind) diff --git a/superset/migrations/versions/2025-11-12_12-54_x2s8ocx6rto6_expand_username_field_to_128_chars.py b/superset/migrations/versions/2025-11-12_12-54_x2s8ocx6rto6_expand_username_field_to_128_chars.py new file mode 100644 index 00000000000..e459e04db68 --- /dev/null +++ b/superset/migrations/versions/2025-11-12_12-54_x2s8ocx6rto6_expand_username_field_to_128_chars.py @@ -0,0 +1,97 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +"""Expand username field to 128 chars + +Revision ID: x2s8ocx6rto6 +Revises: c233f5365c9e +Create Date: 2025-11-12 12:54:00.000000 + +""" + +import logging + +import sqlalchemy as sa +from alembic import op + +from superset.migrations.shared.utils import get_table_column + +logger = logging.getLogger("alembic.env") + +# revision identifiers, used by Alembic. +revision = "x2s8ocx6rto6" +down_revision = "c233f5365c9e" + + +def upgrade(): + """Expand ab_user.username field from 64 to 128 characters.""" + # Check current column length to avoid errors if already upgraded + column_info = get_table_column("ab_user", "username") + + if column_info is None: + logger.warning("Column ab_user.username not found. Skipping migration.") + return + + # Get current length - check the type attribute + current_type = column_info.get("type") + current_length = getattr(current_type, "length", None) + + if current_length is not None and current_length == 128: + logger.info( + "Column ab_user.username already has length %s. Skipping migration.", + current_length, + ) + return + + with op.batch_alter_table("ab_user") as batch_op: + batch_op.alter_column( + "username", + existing_type=sa.String(current_length) + if current_length + else sa.String(64), + type_=sa.String(128), + existing_nullable=False, + ) + + +def downgrade(): + """Revert ab_user.username field from 128 to 64 characters.""" + # Check current column length + column_info = get_table_column("ab_user", "username") + + if column_info is None: + logger.warning("Column ab_user.username not found. Skipping downgrade.") + return + + current_type = column_info.get("type") + current_length = getattr(current_type, "length", None) + + if current_length is not None and current_length <= 64: + logger.info( + "Column ab_user.username already has length %s. Skipping downgrade.", + current_length, + ) + return + + with op.batch_alter_table("ab_user") as batch_op: + batch_op.alter_column( + "username", + existing_type=sa.String(current_length) + if current_length + else sa.String(128), + type_=sa.String(64), + existing_nullable=False, + )