code: def upgrade():
      """
      Previous sqla_viz behaviour when creating a virtual dataset was faulty
      by creating an associated data access permission with [None] on the database name.
  
      This migration revision, fixes all faulty permissions that may exist on the db
      Only fixes permissions that still have an associated dataset (fetch by id)
      and replaces them with the current (correct) permission name
      """
  
      bind = op.get_bind()
      session = orm.Session(bind=bind)
  
      faulty_view_menus = (
          session.query(ViewMenu)
          .join(PermissionView)
          .join(Permission)
          .filter(ViewMenu.name.ilike("[None].[%](id:%)"))
          .filter(Permission.name == "datasource_access")
          .all()
      )
      orphaned_faulty_view_menus = []
      for faulty_view_menu in faulty_view_menus:
          # Get the dataset id from the view_menu name
          match_ds_id = re.match(
              r"\[None\]\.\[.*\]\(id:(\d+)\)", faulty_view_menu.name
          )
          if match_ds_id:
              dataset_id = int(match_ds_id.group(1))
              dataset = session.query(SqlaTable).get(dataset_id)
              if dataset:
                  try:
                      new_view_menu = dataset.get_perm()
                  except Exception:
                      # This can fail on differing SECRET_KEYS
                      return
                  existing_view_menu = (
                      session.query(ViewMenu)
                      .filter(ViewMenu.name == new_view_menu)
                      .one_or_none()
                  )
                  # A view_menu permission with the right name already exists,
                  # so delete the faulty one later
                  if existing_view_menu:
                      orphaned_faulty_view_menus.append(
                          faulty_view_menu
                      )
                  # No view_menu permission with this name exists
                  # so safely change this one
                  else:
                      faulty_view_menu.name = new_view_menu
      # Commit all view_menu updates
      try:
          session.commit()
      except SQLAlchemyError:
          session.rollback()
  
      # Delete all orphaned faulty permissions
      for orphaned_faulty_view_menu in orphaned_faulty_view_menus:
          pvm = (
              session.query(PermissionView)
              .filter(
                  PermissionView.view_menu == orphaned_faulty_view_menu
              )
              .one_or_none()
          )
          if pvm:
              # Removes orphaned pvm from all roles
              roles = (
                  session.query(Role)
                  .filter(Role.permissions.contains(pvm))
                  .all()
              )
              for role in roles:
                  if pvm in role.permissions:
                      role.permissions.remove(pvm)
              # Now it's safe to remove the pvm pair
              session.delete(pvm)
          # finally remove the orphaned view_menu permission
          session.delete(orphaned_faulty_view_menu)
  
      try:
          session.commit()
      except SQLAlchemyError:
          session.rollback()
  
imports: import re
  from alembic import op
  from sqlalchemy import Column, ForeignKey, Integer, orm, Sequence, String, Table, UniqueConstraint
  from sqlalchemy.exc import SQLAlchemyError
  from sqlalchemy.ext.declarative import declarative_base
  from sqlalchemy.orm import backref, relationship
label_desc: Use image node_id 1 for calling a global function with example usage: upgrade() without return types