From 159354d645f24943c68c6c41d96fcdad2331c522 Mon Sep 17 00:00:00 2001 From: Diego Medina Date: Wed, 16 Mar 2022 16:56:44 -0300 Subject: [PATCH 1/2] fix: fix issue when deleting the last saved query or the last executed query --- superset/config.py | 2 +- ...14_add_on_saved_query_delete_tab_state_.py | 66 +++++++++++++++++++ superset/models/sql_lab.py | 4 +- superset/views/sql_lab.py | 24 +++++++ 4 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 superset/migrations/versions/58df9d617f14_add_on_saved_query_delete_tab_state_.py diff --git a/superset/config.py b/superset/config.py index efb5178679d4a..e8031259b5dfd 100644 --- a/superset/config.py +++ b/superset/config.py @@ -1372,7 +1372,7 @@ def SQL_QUERY_MUTATOR( # pylint: disable=invalid-name,unused-argument try: # pylint: disable=import-error,wildcard-import,unused-wildcard-import import superset_config - from superset_config import * # type:ignore + from superset_config import * print(f"Loaded your LOCAL configuration at [{superset_config.__file__}]") except Exception: diff --git a/superset/migrations/versions/58df9d617f14_add_on_saved_query_delete_tab_state_.py b/superset/migrations/versions/58df9d617f14_add_on_saved_query_delete_tab_state_.py new file mode 100644 index 0000000000000..4b8e9070b484c --- /dev/null +++ b/superset/migrations/versions/58df9d617f14_add_on_saved_query_delete_tab_state_.py @@ -0,0 +1,66 @@ +# 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. +"""add_on_saved_query_delete_tab_state_null_constraint" + +Revision ID: 58df9d617f14 +Revises: 7293b0ca7944 +Create Date: 2022-03-16 23:24:40.278937 + +""" + +# revision identifiers, used by Alembic. +revision = "58df9d617f14" +down_revision = "7293b0ca7944" + +import sqlalchemy as sa +from alembic import op + +from superset.utils.core import generic_find_fk_constraint_name + + +def upgrade(): + bind = op.get_bind() + insp = sa.engine.reflection.Inspector.from_engine(bind) + + with op.batch_alter_table("tab_state") as batch_op: + batch_op.drop_constraint( + generic_find_fk_constraint_name("tab_state", {"id"}, "saved_query", insp), + type_="foreignkey", + ) + + batch_op.create_foreign_key( + "saved_query_id", + "saved_query", + ["saved_query_id"], + ["id"], + ondelete="SET NULL", + ) + + +def downgrade(): + bind = op.get_bind() + insp = sa.engine.reflection.Inspector.from_engine(bind) + + with op.batch_alter_table("tab_state") as batch_op: + batch_op.drop_constraint( + generic_find_fk_constraint_name("tab_state", {"id"}, "saved_query", insp), + type_="foreignkey", + ) + + batch_op.create_foreign_key( + "saved_query_id", "saved_query", ["saved_query_id"], ["id"], + ) diff --git a/superset/models/sql_lab.py b/superset/models/sql_lab.py index d2e9b3fefb018..6a3b4ad8bfd7c 100644 --- a/superset/models/sql_lab.py +++ b/superset/models/sql_lab.py @@ -291,7 +291,9 @@ class TabState(Model, AuditMixinNullable, ExtraJSONMixin): hide_left_bar = Column(Boolean, default=False) # any saved queries that are associated with the Tab State - saved_query_id = Column(Integer, ForeignKey("saved_query.id"), nullable=True) + saved_query_id = Column( + Integer, ForeignKey("saved_query.id", ondelete="SET NULL"), nullable=True + ) saved_query = relationship("SavedQuery", foreign_keys=[saved_query_id]) def to_dict(self) -> Dict[str, Any]: diff --git a/superset/views/sql_lab.py b/superset/views/sql_lab.py index 5ec525b9cac73..49336a84a18d6 100644 --- a/superset/views/sql_lab.py +++ b/superset/views/sql_lab.py @@ -20,6 +20,7 @@ from flask_appbuilder.models.sqla.interface import SQLAInterface from flask_appbuilder.security.decorators import has_access, has_access_api from flask_babel import lazy_gettext as _ +from sqlalchemy import and_ from superset import db, is_feature_enabled from superset.constants import MODEL_VIEW_RW_METHOD_PERMISSION_MAP, RouteMethod @@ -228,6 +229,29 @@ def migrate_query( # pylint: disable=no-self-use def delete_query( # pylint: disable=no-self-use self, tab_state_id: int, client_id: str ) -> FlaskResponse: + # Before deleting the query, ensure it's not tied to any + # active tab as the last query. If so, replace the query + # with the latest one created in that tab + tab_state_query = db.session.query(TabState).filter_by( + id=tab_state_id, latest_query_id=client_id + ) + if tab_state_query.count(): + query = ( + db.session.query(Query) + .filter( + and_( + Query.client_id != client_id, + Query.user_id == g.user.get_id(), + Query.sql_editor_id == str(tab_state_id), + ), + ) + .order_by(Query.id.desc()) + .first() + ) + tab_state_query.update( + {"latest_query_id": query.client_id if query else None} + ) + db.session.query(Query).filter_by( client_id=client_id, user_id=g.user.get_id(), From a1e2c11d6969eb35901722ab4c3ebac2f3f412ba Mon Sep 17 00:00:00 2001 From: Diego Medina Date: Thu, 17 Mar 2022 16:45:08 -0300 Subject: [PATCH 2/2] merge migration --- superset/config.py | 2 +- .../58df9d617f14_add_on_saved_query_delete_tab_state_.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/superset/config.py b/superset/config.py index e8031259b5dfd..efb5178679d4a 100644 --- a/superset/config.py +++ b/superset/config.py @@ -1372,7 +1372,7 @@ def SQL_QUERY_MUTATOR( # pylint: disable=invalid-name,unused-argument try: # pylint: disable=import-error,wildcard-import,unused-wildcard-import import superset_config - from superset_config import * + from superset_config import * # type:ignore print(f"Loaded your LOCAL configuration at [{superset_config.__file__}]") except Exception: diff --git a/superset/migrations/versions/58df9d617f14_add_on_saved_query_delete_tab_state_.py b/superset/migrations/versions/58df9d617f14_add_on_saved_query_delete_tab_state_.py index 4b8e9070b484c..220370f828049 100644 --- a/superset/migrations/versions/58df9d617f14_add_on_saved_query_delete_tab_state_.py +++ b/superset/migrations/versions/58df9d617f14_add_on_saved_query_delete_tab_state_.py @@ -17,14 +17,14 @@ """add_on_saved_query_delete_tab_state_null_constraint" Revision ID: 58df9d617f14 -Revises: 7293b0ca7944 +Revises: 6766938c6065 Create Date: 2022-03-16 23:24:40.278937 """ # revision identifiers, used by Alembic. revision = "58df9d617f14" -down_revision = "7293b0ca7944" +down_revision = "6766938c6065" import sqlalchemy as sa from alembic import op