{"record":{"id":"dc551ae490152fcf","repo":"scenic-views/scenic","slug":"cannot-replace-materialized-views","errorCode":null,"errorMessage":"Cannot replace materialized views","messagePattern":"Cannot replace materialized views","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/scenic/statements.rb","lineNumber":168,"sourceCode":"    #\n    # Does not work with materialized views due to lack of database support.\n    #\n    # @param name [String, Symbol] The name of the database view.\n    # @param version [Fixnum] The version number of the view.\n    # @param revert_to_version [Fixnum] The version number to rollback to on\n    #   `rake db rollback`\n    # @return The database response from executing the create statement.\n    #\n    # @example\n    #   replace_view :engagement_reports, version: 3, revert_to_version: 2\n    #\n    def replace_view(name, version: nil, revert_to_version: nil, materialized: false)\n      if version.blank?\n        raise ArgumentError, \"version is required\"\n      end\n\n      if materialized\n        raise ArgumentError, \"Cannot replace materialized views\"\n      end\n\n      sql_definition = definition(name, version)\n\n      Scenic.database.replace_view(name, sql_definition)\n    end\n\n    private\n\n    def definition(name, version)\n      Scenic::Definition.new(name, version).to_sql\n    end\n\n    def materialized_options(materialized)\n      if materialized.is_a? Hash\n        {\n          no_data: materialized.fetch(:no_data, false),\n          side_by_side: materialized.fetch(:side_by_side, false)","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/scenic-views/scenic/blob/f2162dbddb0fb0eb5d4b04d640dcec5303a387e8/lib/scenic/statements.rb#L150-L186","documentation":"Postgres implements CREATE OR REPLACE VIEW for plain views only; there is no CREATE OR REPLACE MATERIALIZED VIEW statement. Because replace_view compiles to exactly that SQL, Scenic raises ArgumentError ('Cannot replace materialized views') whenever materialized is truthy, regardless of server version, rather than emitting SQL the server would reject anyway.","triggerScenarios":"replace_view :reports, version: 2, materialized: true; replace_view :reports, version: 2, materialized: { side_by_side: true } (any truthy value, including a Hash, hits the guard); copy-pasting a plain-view replace_view migration and turning the flag on.","commonSituations":"A team reaches for replace_view on a materialized view to dodge dependency-ordering pain between tangled views; converting a plain view into a materialized one without switching the migration method; templates shared across view kinds.","solutions":["Use the supported path: update_view :reports, version: 2, revert_to_version: 1, materialized: true (drop and recreate, with existing view indexes automatically reapplied)","If lock duration is the concern, update_view :reports, version: 2, materialized: { side_by_side: true } inside a transactional migration swaps atomically with minimal lock time","If replace_view was chosen to untangle view dependencies, order the migrations so dependents update first instead"],"exampleFix":"# before (ArgumentError: Cannot replace materialized views)\nreplace_view :reports, version: 2, materialized: true\n\n# after: supported materialized update path\nupdate_view :reports, version: 2, revert_to_version: 1, materialized: true","handlingStrategy":"validation","validationCode":"view = Scenic.database.views.find { |v| v.name.to_s == 'reports' }\nif view&.materialized\n  update_view :reports, version: 2, materialized: true\nelse\n  replace_view :reports, version: 2\nend","typeGuard":"def materialized_view?(name)\n  Scenic.database.views.find { |v| v.name.to_s == name.to_s }&.materialized == true\nend","tryCatchPattern":null,"preventionTips":["Internalize the split: replace_view is plain-view-only; materialized views always update via update_view ... materialized: true","When converting a plain view to a materialized one, switch the migration method too","Flag materialized: true combined with replace_view as a bug in migration review"],"tags":["postgres","materialized-view","argument-error","rails","scenic"],"backgroundTag":"unsupported-operation","analyzedSha":"f2162dbddb0fb0eb5d4b04d640dcec5303a387e8","analyzedAt":"2026-08-23T09:20:35.041Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}