Skip to content

planner, executor, sessionctx: reject explicit dml on mview / mlog tables#3

Merged
windtalker merged 1 commit intowindtalker:mv_testfrom
wfxr:mv-write-control
Feb 28, 2026
Merged

planner, executor, sessionctx: reject explicit dml on mview / mlog tables#3
windtalker merged 1 commit intowindtalker:mv_testfrom
wfxr:mv-write-control

Conversation

@wfxr
Copy link

@wfxr wfxr commented Feb 28, 2026

What problem does this PR solve?

Issue Number: ref pingcap#18023

Problem Summary:

Materialized views and their aux tables ($mlog$...) are internal implementation details, but users can currently write them explicitly, which leaks internal mechanisms and can break MV/MV log invariants. We should reject explicit writes and only allow MV maintenance (REFRESH/PURGE) and base-table DML side-effects to touch MV/MV log data.

What changed and how does it work?

Core mechanism

Introduce a MViewInternalDML enum on SessionVars / StatementContext to distinguish user-issued DML from internal maintenance operations. The function CheckMViewUpdatable() checks the target table's metadata and rejects the operation unless the appropriate context flag is set.

Planner-layer guard (authoritative)

The planner is the single authoritative guard that rejects DML on MV/mlog tables. It covers all DML paths:

  • INSERT/REPLACE (planbuilder.go)
  • UPDATE (logical_plan_builder.go)
  • DELETE (logical_plan_builder.go)
  • LOAD DATA (planbuilder.go)
  • IMPORT INTO (planbuilder.go)

This catches errors early, including at PREPARE time.

Executor-layer assertions (defense-in-depth)

The executor build phase contains intest.AssertNoError assertions that verify the planner already rejected invalid operations. These are no-ops in production builds and only fire in test builds (via the intest build tag), catching developer bugs where a code path bypasses the planner guard.

Covered operations: INSERT/REPLACE, UPDATE, DELETE, LOAD DATA, IMPORT INTO (builder.go).

Bypass rules

Target table Allowed when Example
MV table IsMViewRefreshContext() REFRESH MATERIALIZED VIEW
MV log table IsMViewPurgeContext() PURGE MATERIALIZED VIEW LOG
Base table with mlog Always (mlog writes are side-effects via mlogTable wrapper) INSERT INTO base_table ...

Error handling

Uses existing ErrNonUpdatableTable for consistency with views and sequences (no new error codes).

Tradeoffs / limitations

  • Scope is intentionally DML-focused — does not gate TRUNCATE or DDL yet.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No need to test
    • I checked and no code files have been changed.

Side effects

  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Breaking backward compatibility

Documentation

  • Affects user behaviors
  • Contains syntax changes
  • Contains variable changes
  • Contains experimental features
  • Changes MySQL compatibility

Release note

Please refer to Release Notes Language Style Guide to write a quality release note.

Disallow explicit DML (INSERT/REPLACE/UPDATE/DELETE) and bulk operations (LOAD DATA/IMPORT INTO) on materialized view and materialized view log tables. Only internal maintenance operations (REFRESH/PURGE) can modify these tables.

Materialized views and their $mlog$ tables are internal implementation
details. Users should not be able to write them directly; only MV
maintenance (REFRESH/PURGE) and base-table DML side-effects should
touch MV/MV log data.

This change introduces a planner-layer guard (CheckMViewUpdatable) as
the single authoritative check that rejects explicit DML (INSERT,
REPLACE, UPDATE, DELETE, LOAD DATA, IMPORT INTO) targeting MV or MV
log tables. The executor build path contains defense-in-depth
assertions (intest.AssertNoError) that verify the planner guard in
test builds.

Bypass rules:
- Writes to MV/MV log tables are only allowed when both
  InMaterializedViewMaintenance and InRestrictedSQL are set, i.e. from
  internal sessions used by maintenance operations (REFRESH/PURGE)
- Base-table DML with mlog side-effects works normally

Uses existing ErrNonUpdatableTable for consistency with views and
sequences. Scope is intentionally DML-focused (does not gate
TRUNCATE/DDL yet).
@wfxr wfxr changed the title planner, executor, sessionctx: reject explicit DML on MV/MV log tables planner, executor, sessionctx: reject explicit dml on mview / mlog tables Feb 28, 2026
Copy link
Owner

@windtalker windtalker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@windtalker windtalker merged commit c052b25 into windtalker:mv_test Feb 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants