Commit Graph
1521 Commits
Author SHA1 Message Date
rusackasandClaude Opus 4.8 85c52165ab experiment: migrate Query.get() calls to session.get() for SQLAlchemy 2.0
session.query(Model).get(id) is deprecated under SQLAlchemy 2.0 in
favor of session.get(Model, id). Migrate the 9 remaining call sites
(flagged by review) and update the corresponding test mocks that
asserted against the old session.query(...).get(...) call shape.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-10 04:40:01 -07:00
Claude Code 6ec8ffecb2 experiment: give mocked Dashboard class real relationship attrs in generate_dashboard tests
`generate_dashboard()` re-fetches with eager-loaded relationships via
`subqueryload(Dashboard.slices).subqueryload(Slice.editors)` (and
similarly for `.tags`, `Dashboard.editors`, `Dashboard.tags`), using a
`Dashboard` class resolved through a deferred `from
superset.models.dashboard import Dashboard` import inside the function --
deferred specifically so `@patch("superset.models.dashboard.Dashboard")`
in tests can substitute it.

`test_generate_dashboard_refetches_via_dao` and
`test_generate_dashboard_restricted_user_redacts_chart_datasource_name`
both patch the whole `Dashboard` class to control its constructor
(`mock_dashboard_cls.return_value = dashboard`), which leaves
`Dashboard.slices`/`.editors`/`.tags` as unconfigured auto-mocks. Passing
those to `subqueryload()` now raises `sqlalchemy.exc.ArgumentError:
Wildcard token cannot be followed by another entity` -- `ArgumentError`
subclasses `SQLAlchemyError`, which `generate_dashboard`'s re-fetch
already catches and treats as "re-fetch failed, return minimal
response", so both tests silently got the fallback response instead of
the real one they were asserting against.

Confirmed via a side-by-side instrumented run against SQLAlchemy 1.4.54:
the exact same mock chain there raised no error at all -- 1.4 didn't
eagerly validate `subqueryload()`'s argument type, so an unconfigured
MagicMock silently worked. This is a genuine SQLAlchemy 2.0 strictness
change in loader-option construction, but it doesn't affect real
production code (where `Dashboard` is never mocked and `.slices` is
always a real `InstrumentedAttribute`) -- it only breaks these two
tests' mocks, which were never fully specified to begin with.

Capture the real `Dashboard` class in a module-level `_RealDashboard`
import (before any per-test `@patch` can shadow it) and copy its
`slices`/`editors`/`tags` relationship attributes onto the mocked class
in `_setup_generate_dashboard_mocks`, so `subqueryload()` sees genuine
mapped attributes while `Dashboard(...)` construction stays mocked. Ran
the full 64-test file afterward, including the sibling
`..._refetch_sqlalchemy_error_rollback` test (which forces a real
`SQLAlchemyError` via `find_by_id.side_effect` and still correctly hits
the fallback path) -- no regressions.
2026-08-10 04:40:00 -07:00
Claude Code ab1e628eef experiment: fix under-specified query mock and stale server-name in oauth2 sql_lab test
test_get_sql_results_oauth2's `query = mocker.MagicMock(select_as_cta=False,
database=database)` left `limit` and `select_as_cta_used` as unconfigured
auto-mocks. `execute_sql_statements` unconditionally calls `apply_limit`
before ever reaching the mocked OAuth2 error, and `apply_limit` does
`query.limit > sql_max_row` once `SQLLAB_CTAS_NO_LIMIT` is false -- an
unconfigured MagicMock has no real `__gt__`, so this raised `TypeError:
'>' not supported between instances of 'MagicMock' and 'int'` instead of
ever reaching the OAuth2 path.

Root-caused with a side-by-side instrumented run against master pinned to
SQLAlchemy 1.4.54 + Flask-SQLAlchemy 2.5.1 (byte-identical sql_lab.py and
test file): there, `flask.current_app` inside `apply_limit` resolved to
an unrelated, already-initialized `superset.app` singleton (config
SQLLAB_CTAS_NO_LIMIT=True) instead of this test's own fixture app
(SQLLAB_CTAS_NO_LIMIT=False), so `apply_limit` took its
`select_as_cta_used and sqllab_ctas_no_limit` early-return path before
ever touching `query.limit` -- masking the gap by accident. Under
Flask-SQLAlchemy 3.x, `current_app` correctly resolves to the test's own
pushed app context, so the early return no longer fires and the
underspecified mock's real gap is exposed. This is not a SQLAlchemy 2.0
semantic break in application code; it's an under-specified test double
that only ever worked because of unrelated cross-test app-identity
bleed in the old environment.

Set `limit=None` and `select_as_cta_used=False` on the mock, matching
the real `Query` model's actual column defaults (`Column(Integer)` is
nullable/None; `Column(Boolean, default=False)`), so `apply_limit`
behaves the same way it would for a genuine freshly-queried `Query` row.

With the mock now correctly reaching the OAuth2 path inside the right
app context, the generated redirect URL correctly reflects this test's
own `SERVER_NAME=example.com` fixture config rather than Flask's
"localhost" default that leaked in from the wrong app previously --
update the two hardcoded `http://localhost/...` expectations to
`http://example.com/...` to match.
2026-08-10 04:40:00 -07:00
Claude Code e2093a6140 experiment: update expected DatabaseFilter compiled-SQL column order for SQLAlchemy 2.0
SQLAlchemy 2.0's declarative mapper now orders a model's mapped columns
with the class's own Column attributes first, followed by mixin-provided
columns (AuditMixinNullable's created_on/changed_on/created_by_fk/
changed_by_fk and the uuid mixin column) in mixin-declaration order,
appended at the end rather than interleaved at the front. This shifted
where `dbs.uuid`, `dbs.created_on`, `dbs.changed_on` (and the matching
`ssh_tunnels_1.*` aliases from the joined SSHTunnel) land in the compiled
SELECT list. SQLAlchemy 2.0 also renders the concatenation expression on
the left of an IN clause with an explicit parenthesized group.

Verified the actual compiled query has the identical column set (just
reordered) and an equivalent WHERE clause (same expression, extra
parens), then updated the hardcoded expected string in
test_database_filter to match.
2026-08-10 04:40:00 -07:00
Claude Code 3488760f07 experiment: update shillelagh error-URL assertions from SQLAlchemy 14 to 20 doc path
shillelagh.exceptions.ProgrammingError is wrapped by SQLAlchemy's own
StatementError machinery, which appends a "Background on this error at:
https://sqlalche.me/e/<major><minor>/<code>" hint derived from the
running SQLAlchemy version. Under 1.4 that was .../e/14/f405; under 2.0
it's .../e/20/f405. test_dml and test_allowed_dbs hardcoded the 1.4 URL
in their expected exception strings; update both to the 2.0 path.
2026-08-10 04:39:59 -07:00
Claude Code b19cf53797 experiment: stop passing password="***" to real database connections and caches
Critical regression: SQLAlchemy 2.0 changed URL.__str__() to always
substitute "***" for the password rather than rendering it verbatim
(SQLAlchemy 1.4's str(URL) rendered the real value). Every
build_sqlalchemy_uri() implementation across the db_engine_specs
(base/Postgres+MySQL+etc, ClickHouse, Databricks x2, Snowflake,
Databend, Couchbase) built a URL with the user's real password and
returned str(url) - which is exactly the string
superset/databases/schemas.py's pre-load hook writes into
data["sqlalchemy_uri"] when a database is created or edited via the
parameterized connection form. Under SQLAlchemy 2.0 that stores the
literal password "***" instead of the real one, breaking every new
connection made that way. Switched all of these to
render_as_string(hide_password=False), which is the 2.0-native way to
get the real, unmasked URL string.

Two more instances of the same str(URL) regression in
superset/models/core.py, both with real functional impact:

- Database.set_sqlalchemy_uri() intentionally replaces the real
  password with Superset's own PASSWORD_MASK sentinel
  ("X" * 10, not a secret) before storing self.sqlalchemy_uri, so a
  later edit can compare conn.password != PASSWORD_MASK to detect
  "the user didn't touch the password field, keep the existing one."
  str(conn) under 2.0 was substituting its own "***" for that
  sentinel, so the stored URI no longer round-tripped to
  PASSWORD_MASK - it round-tripped to the meaningless literal "***",
  breaking password-preservation on every database edit.

- The per-process SQLAlchemy engine cache (superset/models/core.py,
  _ENGINE_CACHE) keys on str(sqlalchemy_url) specifically so that a
  password rotation naturally invalidates the cached engine (the
  module comment states this explicitly). Under 2.0, str(url) always
  masks to the same "***" regardless of the real password, so
  rotating a database's password would silently keep reusing the old,
  now-wrong cached engine/connection pool for the life of the worker
  process.

Also fixed a separate, unrelated 1.4->2.0 break in
superset/db_engine_specs/duckdb.py: two build_sqlalchemy_uri variants
called the raw URL(...) constructor, which SQLAlchemy 2.0 turned into
a strict NamedTuple requiring username/password/host/port to be
passed explicitly (they used to default to None). That raised
"URL.__new__() missing 4 required positional arguments" outright.
Switched both to URL.create(), which keeps those optional.

Test-side: model_tests.py/db_engine_specs test files that asserted
str(uri) == "<scheme>://user:realpassword@host/..." were relying on
the old unmasked str() behavior; switched them to
uri.render_as_string(hide_password=False) to keep verifying the real
underlying value rather than relaxing what's being checked.

Verified locally (sqlite): tests/integration_tests/db_engine_specs/
and tests/unit_tests/db_engine_specs/ - the password-masking and
duckdb URL() failures are gone (18 -> 12 remaining, unrelated:
mysqlclient not importable on this Mac, 5 bigquery test_fetch_data
failures, and a where_latest_partition literal-rendering cluster
across hive/presto/trino, tracked separately).
2026-08-10 04:39:57 -07:00
Dang DaoandEvan Rusackas bf294cfa43 perf(dashboard): avoid query context N+1 queries (#42474)
Co-authored-by: Evan Rusackas <evan@preset.io>
2026-08-08 17:27:04 -07:00
42e4030104 feat(security): add guest user attributes and get_guest_user_attribute() macro (#33924)
Co-authored-by: Yash Janoria <yash.janoria@314ecorp.com>
Co-authored-by: Evan <evan@preset.io>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Evan Rusackas <evan@rusackas.com>
2026-08-08 16:56:07 -07:00
8181917f79 fix: Apply timezone offset to convert local time boundaries to UTC (#37014)
Co-authored-by: Evan Rusackas <evan@rusackas.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-08 16:40:31 -07:00
4a587b8539 feat(security): actionable, request-access-aware data permission errors (#41843)
Co-authored-by: Greg Neighbors <gregneighbors@Gregs-Air-2.lan>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Greg Neighbors <gregneighbors@Gregs-MacBook-Air-2.local>
2026-08-08 11:51:15 -07:00
2a9c5acb01 chore(deps): bump numpy from 1.26.4 to 2.4.6 (#42778)
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Joe Li <joe@preset.io>
Co-authored-by: rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-07 22:55:20 -07:00
Bart SkowronandClaude Fable 5 bd03440ac8 feat(alerts-reports): add per-schedule toggle to include/exclude the Explore in Superset link (#42494)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 21:05:16 -07:00
Elizabeth ThompsonandClaude Opus 4.8 eb8592d4bf fix(sqla): don't mislabel DB errors as ColumnNotFoundException in adhoc_column_to_sqla (#42889)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-07 15:03:49 -07:00
Joe LiandClaude Opus 4.8 c901f01693 fix(datasets): preserve metric/column uuids on dataset export (#42393)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-07 11:15:24 -07:00
4c894affb2 fix(models): purge_oauth2_tokens filters on wrong column (#42211)
Signed-off-by: Martin Brodeur <addressedemartin@gmail.com>
Co-authored-by: rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-07 11:04:11 -07:00
Mike BridgeandMike Bridge 280253b1fb fix(deletion-retention): dedupe repeated blocked audits (#42863)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
2026-08-07 10:11:36 -07:00
fe06ebe796 feat(versioning): enable version history and capture by default (#42801)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:25:48 -03:00
Mehmet Salih Yavuz 14583b6a5f fix(themes): serve system themes with the algorithm of the slot they fill (#42700) 2026-08-07 14:29:49 +03:00
Elizabeth Thompson 7dd6ce835f fix(datasets): catch TemplateError instead of narrower TemplateSyntaxError in render_dataset_fields (#42802) 2026-08-06 15:05:08 -07:00
4f2147009f feat(dashboard): expand all chart descriptions (#32958)
Co-authored-by: Urban Pettersson <urban.pettersson@alteryx.com>
Co-authored-by: rusackas <evan@rusackas.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 14:47:30 -07:00
Evan RusackasandClaude Code 2ecce20e48 chore(viz): remove legacy explore_json + viz.py pipeline (#41714)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-06 13:22:15 -07:00
Elizabeth ThompsonandClaude 01a9fdc621 chore(reports): thread cache-key/execution-id log context through screenshot capture logs (#42657)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-06 09:43:12 -07:00
ba09f399ac feat(soft-delete): enable soft delete by default and purge for real (#42800)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 09:34:55 -07:00
Pat BuxtonandClaude Opus 5 de93a19b3c fix(engine): update databend engine spec for dialect version >=0.4.6 (#28627)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 06:28:16 -07:00
Joe Li b8c44a1ad5 fix(ci): restore master validation checks (#42807) 2026-08-05 22:58:05 -07:00
JUST.in DO ITandClaude Sonnet 5 d594a4d157 fix(mcp): make streamable-http session mode configurable via MCP_STATELESS_HTTP (#42814)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 21:31:22 -07:00
Evan RusackasandClaude Code 1de35d1361 fix(mcp): reject unknown fields in nested chart-config models too (#42626) (#42732)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-05 16:24:55 -07:00
Evan RusackasandClaude Code 3f011edabb fix(mcp): supply required DBEventLogger args so mcp_tool_error events are logged (#42579) (#42730)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-05 16:22:10 -07:00
32e4e3c6a8 fix(reports): enforce dashboard readiness and execution budget (#42624)
Co-authored-by: Matt Fitzgerald <matt.fitzgerald@preset.io>
Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-05 14:30:35 -07:00
Evan RusackasandClaude Opus 4.8 633f393880 feat(pivot-table): reintroduce show-values-as-fraction display option (#42761)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-05 14:17:30 -07:00
Amin GhadersohiandClaude 0e4e368768 feat(mcp): add observability to MCP service (#41921)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-05 11:52:34 -07:00
126c93b495 fix(versioning): pin shadow-row reads and restore to (id, uuid) (#42797)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 11:12:20 -07:00
e880711bbb fix(migrations): actually drop _customer_location_uc (list == set no-op) (#42642)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 11:12:14 -07:00
47a4dabd14 feat(soft-delete): warn at startup when a retention task's Celery config is incomplete (#42641)
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 11:12:09 -07:00
Evan RusackasandClaude Code f65b42408e fix(tasks): gate exception_type in task properties behind SHOW_STACKTRACE (#40587)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-05 09:31:30 -07:00
Evan RusackasandClaude Code e7338a2add fix(mcp): route FastMCP ValidationError through the validation error handler (#42578) (#42738)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-05 09:27:28 -07:00
Evan RusackasandClaude Code 9c97d4859c fix(mcp): stop false-flagging successful responses with error_type:null as failures (#42580) (#42736)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-05 09:27:17 -07:00
endimonan 1605676aa7 fix(reports): use zip extension for bundled CSV attachments (#42640) 2026-08-04 21:09:19 -07:00
Abdul Rehman 956231b9ee fix(dataset): make post-save column refresh best-effort for Jinja SQL (#42463) 2026-08-04 20:40:09 -07:00
Evan RusackasandClaude Code 2f9bde5579 fix(sql): preserve quoted-identifier casing for the HANA dialect (#39328) (#42731)
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-04 20:39:35 -07:00
Amin Ghadersohi 86b2076169 fix(mcp): handle missing metrics/columns and null data in chart preview (#42576) 2026-08-04 19:59:47 -04:00
Amin GhadersohiandClaude 043163b54c fix(mcp): reject unparseable time_range instead of silently matching full table (#42283)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-04 19:45:30 -04:00
Amin Ghadersohi 33c386a2d6 fix(core): reduce metric expression log noise (#42619) 2026-08-04 16:35:02 -07:00
Elizabeth Thompson 7c03736623 fix(alerts): wrap Jinja rendering errors in AlertCommand._execute_query (#42714) 2026-08-04 15:07:10 -07:00
Elizabeth Thompson 4e9e884dd8 fix(sqllab): wrap process_template() in QueryEstimationCommand to prevent raw UndefinedError leak (#42757) 2026-08-04 15:02:47 -07:00
Rafael BenitezandClaude Opus 4.8 1478e32bc2 fix(mcp): persist Handlebars template under camelCase key so it renders (#42725)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 16:21:57 -04:00
Alexandru Soare 3dde95d809 feat(reports): add retry on failure support for reports (#42481) 2026-08-04 18:04:32 +03:00
Yuriy Krasilnikov 457cd3487d fix(api): include query lifecycle timing in /api/v1/chart/data response (#37516) 2026-08-03 17:27:57 -07:00
Elizabeth ThompsonandClaude 03b35186e5 fix(jinja): handle UndefinedError from virtual dataset templates (#42366)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-03 17:09:31 -07:00
Elizabeth ThompsonandClaude 25ab96188f fix(sqllab): roll back session before retrying get_query after a broken transaction (#42675)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-03 15:04:28 -07:00