Using WITH and an sql request breaks undo functionaity

Steps to reproduce:

  1. Review a card from a deck with multiple cards due

  2. In the debug console, execute the following request:

    mw.col.db.all("WITH e AS (SELECT ease FROM revlog) SELECT ease FROM revlog")
    

    (yes, the table defined by the WITH isn’t even used)

  3. Press undo in the reviewer (in won’t have any effect):

    image

Going through the same steps, but shortening the request to just “SELECT ease FROM revlog” does not trigger the issue (the previous card rating gets undone as expected).


Calling the same request with an add-on

minimal __init__.py code:
from aqt import mw, gui_hooks

def break_undo(*args, **kwargs):
    mw.col.db.all("WITH e AS (SELECT ease FROM revlog) SELECT ease FROM revlog")

gui_hooks.reviewer_did_show_question.append(break_undo)

makes the undo button permanently grayed out during reviews:

image

Just as with a standalone command, removing the “WITH …” part from the query string prevents the issue from appearing


Tested in 26.05 (e64c6b1a)⁩ on win 11 (26200.8655) with all add-ons turned off

The backend has a very simple check for mutating SQL commands: anki/rslib/src/backend/dbproxy.rs at f6ac1985ba8c05405289b5a4454767de12208e31 · ankitects/anki · GitHub

Any command not starting with “select” is considered mutating and clears the undo/study queues for safety.

In that case, the workaround is to wrap anything else in a dummy SELECT * FROM (...), I suppose