Skip to content

.transform() fails to drop column if table is part of a view #586

Closed

Description

@simonw

I got this error trying to drop a column from a table that was part of a SQL view:

error in view plugins: no such table: main.pypi_releases

Upon further investigation I found that this pattern seemed to fix it:

def transform_the_table(conn):
    # Run this in a transaction:
    with conn:
        # We have to read all the views first, because we need to drop and recreate them
        db = sqlite_utils.Database(conn)
        views = {v.name: v.schema for v in db.views if table.lower() in v.schema.lower()}
        for view in views.keys():
            db[view].drop()
        db[table].transform(
            types=types,
            rename=rename,
            drop=drop,
            column_order=[p[0] for p in order_pairs],
        )
        # Now recreate the views
        for name, schema in views.items():
            db.create_view(name, schema)

So grab a copy of any view that might reference this table, start a transaction, drop those views, run the transform, recreate the views again.

I wonder if this should become an option in sqlite-utils? Maybe a recreate_views=True argument for table.tranform(...)? Should it be opt-in or opt-out?

Originally posted by @simonw in simonw/datasette-edit-schema#35 (comment)

Activity

  1. simonw commented on Aug 18, 2023

    @simonw
    OwnerAuthor

    More notes in here:

    Not all Python/SQLite installations exhibit this problem by default!

    It turns out this is controlled by the legacy_alter_table pragma: https://sqlite.org/pragma.html#pragma_legacy_alter_table

    If that PRAGMA is turned on (default in newer SQLites) then alter table will error if you try to rename a table that is referenced in a view.

    Here's a one-liner to test if it is on or not:

    python -c 'import sqlite3; print(sqlite3.connect(":memory:").execute("PRAGMA legacy_alter_table").fetchall())'
  2. simonw commented on Aug 18, 2023

    @simonw
    OwnerAuthor

    Options:

    • Provide a recreate_views: bool parameter to table.transform() controlling if views that might reference this table are stashed and dropped and recreated within a transaction as part of the operation. But should that be True or False by default?
    • Read that PRAGMA and automatically do that view workaround if it's turned on
    • Toggle that PRAGMA off for the duration of the .transform() operation and on again at the end. Does it only affect the current connection?
    • Try the transform() in a transaction, detect the "error in view", "no such table"error, if spotted then do the VIEW workaround and try again

    I'm on the fence as to which of these I like the most. I'm tempted to go with the one which just drops VIEWS and recreates them all the time, because it feels simpler.

  3. added a commit that references this issue on Aug 18, 2023
  4. simonw commented on Aug 18, 2023

    @simonw
    OwnerAuthor

    I shipped the view recreating fix in datasette-edit-schema, so at least I can start exercising that fix and see if it has any weird issues.

  5. tobych commented on Apr 29, 2024

    @tobych

    Just noting that the fix @simonw mentions is here in datasette-edit-schema.

    I'm using views that reference tables that are changing, so I'm likely to want this fix.

  6. tobych commented on Apr 29, 2024

    @tobych

    The documentation for the legacy_alter_table pragma states that the behavior it controls is per-connection, and not persistent.

    Regardless, using the pragma seems a bad idea to me, because:

    • the feature it's controlling is a legacy behavior
    • the documentation says: "New applications should leave this flag turned off."

    The recreate_views idea seems like it might be useful, but I reckon You Ain't Gonna Need It. If someone has a lot of views, and it takes ages to recreate them, perhaps. That seems highly unlikely.

  7. simonw commented on Aug 13, 2026

    @simonw
    OwnerAuthor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or requesttransformThe table.transform() mechanism.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions