Skip to content

Commit

Permalink
Fixes getredash#6767: correctly rehash queries in a migration (getred…
Browse files Browse the repository at this point in the history
  • Loading branch information
arikfr authored Oct 25, 2024
1 parent d8dde6c commit ba973eb
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 2 deletions.
64 changes: 64 additions & 0 deletions migrations/versions/9e8c841d1a30_fix_hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""fix_hash
Revision ID: 9e8c841d1a30
Revises: 7205816877ec
Create Date: 2024-10-05 18:55:35.730573
"""
import logging
from alembic import op
import sqlalchemy as sa
from sqlalchemy.sql import table
from sqlalchemy import select

from redash.query_runner import BaseQueryRunner, get_query_runner


# revision identifiers, used by Alembic.
revision = '9e8c841d1a30'
down_revision = '7205816877ec'
branch_labels = None
depends_on = None


def update_query_hash(record):
should_apply_auto_limit = record['options'].get("apply_auto_limit", False) if record['options'] else False
query_runner = get_query_runner(record['type'], {}) if record['type'] else BaseQueryRunner({})
query_text = record['query']

parameters_dict = {p["name"]: p.get("value") for p in record['options'].get('parameters', [])} if record.options else {}
if any(parameters_dict):
print(f"Query {record['query_id']} has parameters. Hash might be incorrect.")

return query_runner.gen_query_hash(query_text, should_apply_auto_limit)


def upgrade():
conn = op.get_bind()

metadata = sa.MetaData(bind=conn)
queries = sa.Table("queries", metadata, autoload=True)
data_sources = sa.Table("data_sources", metadata, autoload=True)

joined_table = queries.outerjoin(data_sources, queries.c.data_source_id == data_sources.c.id)

query = select([
queries.c.id.label("query_id"),
queries.c.query,
queries.c.query_hash,
queries.c.options,
data_sources.c.id.label("data_source_id"),
data_sources.c.type
]).select_from(joined_table)

for record in conn.execute(query):
new_hash = update_query_hash(record)
print(f"Updating hash for query {record['query_id']} from {record['query_hash']} to {new_hash}")
conn.execute(
queries.update()
.where(queries.c.id == record['query_id'])
.values(query_hash=new_hash))


def downgrade():
pass
16 changes: 16 additions & 0 deletions redash/cli/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@
manager = AppGroup(help="Queries management commands.")


@manager.command(name="rehash")
def rehash():
from redash import models

for q in models.Query.query.all():
old_hash = q.query_hash
q.update_query_hash()
new_hash = q.query_hash

if old_hash != new_hash:
print(f"Query {q.id} has changed hash from {old_hash} to {new_hash}")
models.db.session.add(q)

models.db.session.commit()


@manager.command(name="add_tag")
@argument("query_id")
@argument("tag")
Expand Down
4 changes: 2 additions & 2 deletions redash/metrics/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from sqlalchemy.engine import Engine
from sqlalchemy.event import listens_for
from sqlalchemy.orm.util import _ORMJoin
from sqlalchemy.sql.selectable import Alias
from sqlalchemy.sql.selectable import Alias, Join

from redash import statsd_client

Expand All @@ -18,7 +18,7 @@ def _table_name_from_select_element(elt):
if isinstance(t, Alias):
t = t.original.froms[0]

while isinstance(t, _ORMJoin):
while isinstance(t, _ORMJoin) or isinstance(t, Join):
t = t.left

return t.name
Expand Down

0 comments on commit ba973eb

Please sign in to comment.