{"record":{"id":"8cf49071b4653e93","repo":"apache/cassandra","slug":"cannot-drop-column-s-on-base-table-s-with-materi","errorCode":null,"errorMessage":"Cannot drop column %s on base table %s with materialized views","messagePattern":"Cannot drop column (.+?) on base table (.+?) with materialized views","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java","lineNumber":516,"sourceCode":"                return;\n            }\n\n            if (currentColumn.isPrimaryKeyColumn())\n                throw ire(\"Cannot drop PRIMARY KEY column %s\", column);\n\n            /*\n             * Cannot allow dropping top-level columns of user defined types that aren't frozen because we cannot convert\n             * the type into an equivalent tuple: we only support frozen tuples currently. And as such we cannot persist\n             * the correct type in system_schema.dropped_columns.\n             */\n            if (currentColumn.type.isUDT() && currentColumn.type.isMultiCell())\n                throw ire(\"Cannot drop non-frozen column %s of user type %s\", column, currentColumn.type.asCQL3Type());\n\n            if (!table.indexes.isEmpty())\n                AlterTableStatement.validateIndexesForColumnModification(table, column, false);\n\n            if (!isEmpty(keyspace.views.forTable(table.id)))\n                throw ire(\"Cannot drop column %s on base table %s with materialized views\", currentColumn, table.name);\n\n            builder.removeRegularOrStaticColumn(column);\n            builder.recordColumnDrop(currentColumn, getTimestamp());\n        }\n\n        /**\n         * @return timestamp from query, otherwise return current time in micros\n         */\n        private long getTimestamp()\n        {\n            // Prior to Metadata serialization V5, the execution timestamp was not included in AlterSchema\n            // serializations. Instead, the current time (from ClientState::getTimestamp) was used, making\n            // DROP COLUMN non-idempotent and causing potenial data loss as described in CASSANDRA-18961.\n            // This was fixed before release by serialization V5, but we include a dangerous backwards\n            // compatibility option here or so that we can still apply pre-V5 serialized transformations\n            // (which would only exist in clusters running pre-release versions of Cassandra). Once all peers\n            // are running a V5 compatible version, ClientState::getTimestamp will never be used.\n            return timestamp == null ? fixedTimestampMicros().orElseGet(ClientState::getTimestamp) : timestamp;","sourceCodeStart":498,"sourceCodeEnd":534,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java#L498-L534","documentation":"This InvalidRequestException is thrown when attempting to DROP a column on a base table that has one or more materialized views (or similar views) defined on it. Views derive their data from the base table's columns, so Cassandra refuses column drops that would break the view definitions.","triggerScenarios":"Executing `ALTER TABLE ks.base DROP col` when `keyspace.views.forTable(table.id)` is non-empty, i.e. any materialized view (or SASI-style view) exists over that base table. This applies even if the dropped column is not referenced in the view's SELECT or PRIMARY KEY.","commonSituations":"Developers who added a materialized view for query patterns and later try to evolve the base table schema; migration scripts written before the view existed; confusion because secondary indexes are checked per-column but materialized views block all column drops on the base table.","solutions":["Drop all materialized views on the table first (`DROP MATERIALIZED VIEW ks.view_name`), perform the column drop, then recreate the views with the new schema (views can be rebuilt from existing base data).","If the view is no longer needed, just drop it and proceed with the ALTER TABLE.","List existing views with `SELECT view_name FROM system_schema.views WHERE keyspace_name='ks' AND table_name='tbl'` to identify blockers.","Restructure so views are created on a new table version if frequent column changes are expected."],"exampleFix":"// before\nALTER TABLE users DROP last_login; -- users has MV users_by_login: fails\n// after\nDROP MATERIALIZED VIEW ks.users_by_login;\nALTER TABLE users DROP last_login;\nCREATE MATERIALIZED VIEW ks.users_by_login AS SELECT ... FROM users WHERE ...;","handlingStrategy":"validation","validationCode":"// Pre-check for materialized views on the base table\nResultSet rs = session.execute(\n  \"SELECT view_name FROM system_schema.views WHERE keyspace_name=? AND table_name=?\",\n  ks, table);\nboolean hasViews = rs.iterator().hasNext();\n// if hasViews, drop/recreate views around the ALTER TABLE","typeGuard":null,"tryCatchPattern":"try {\n    session.execute(\"ALTER TABLE ks.tbl DROP \" + column);\n} catch (com.datastax.oss.driver.api.core.servererrors.InvalidQueryException e) {\n    if (e.getMessage().contains(\"with materialized views\")) {\n        // drop MVs, retry ALTER, recreate MVs\n    } else throw e;\n}","preventionTips":["Query system_schema.views for the table before any column-level migration.","Include DROP/CREATE MATERIALIZED VIEW steps in migration scripts when views exist.","Minimize use of materialized views; prefer explicitly managed denormalized tables that you control.","Sequence migrations: drop views, alter table, rebuild views."],"tags":["cql","schema","materialized-view","alter-table"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}