{"record":{"id":"11d6bdef78419c1e","repo":"prestodb/presto","slug":"materialized-view-is-recursive","errorCode":"MATERIALIZED_VIEW_IS_RECURSIVE","errorMessage":"Materialized view is recursive","messagePattern":"Materialized view is recursive","errorType":"error_code","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java","lineNumber":2453,"sourceCode":"                            NOT_SUPPORTED,\n                            table,\n                            \"INSERT into table %s by selecting from materialized view %s is not supported because %s is a base table of the materialized view\",\n                            targetTable,\n                            optionalMaterializedView.get().getTable(),\n                            targetTable);\n                }\n            }\n            Statement statement = analysis.getStatement();\n            if (optionalMaterializedView.isPresent() && (statement instanceof Query || statement instanceof Insert || statement instanceof CreateTableAsSelect)) {\n                if (isMaterializedViewDataConsistencyEnabled(session) || !isLegacyMaterializedViews(session)) {\n                    // When the materialized view has already been expanded, do not process it. Just use it as a table.\n                    MaterializedViewAnalysisState materializedViewAnalysisState = analysis.getMaterializedViewAnalysisState(table);\n\n                    if (materializedViewAnalysisState.isNotVisited()) {\n                        return processMaterializedView(table, name, scope, optionalMaterializedView.get());\n                    }\n                    if (materializedViewAnalysisState.isVisited()) {\n                        throw new SemanticException(MATERIALIZED_VIEW_IS_RECURSIVE, table, \"Materialized view is recursive\");\n                    }\n                }\n                else {\n                    // when stitching is not enabled, still check permission of each base table\n                    MaterializedViewDefinition materializedViewDefinition = optionalMaterializedView.get();\n                    analysis.getViewDefinitionReferences().addMaterializedViewDefinitionReference(name, materializedViewDefinition);\n\n                    Query viewQuery = (Query) sqlParser.createStatement(\n                            materializedViewDefinition.getOriginalSql(),\n                            createParsingOptions(session, warningCollector));\n\n                    analysis.registerMaterializedViewForAnalysis(name, table, materializedViewDefinition.getOriginalSql());\n                    process(viewQuery, scope);\n                    analysis.unregisterMaterializedViewForAnalysis(table);\n                }\n            }\n\n            TableColumnMetadata tableColumnsMetadata = getTableColumnsMetadata(session, metadataResolver, analysis.getMetadataHandle(), name);","sourceCodeStart":2435,"sourceCodeEnd":2471,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java#L2435-L2471","documentation":"Presto throws this semantic error when analysis of a materialized view detects that the view references itself, directly or transitively, through another materialized view. The analyzer tracks visited materialized views via MaterializedViewAnalysisState; encountering a table already marked visited means a cycle exists. Materialized view definitions must form a DAG, so recursion is rejected at analysis time.","triggerScenarios":"Creating or querying a materialized view whose definition query selects from itself, or from a chain of materialized views that loops back to it (e.g. CREATE MATERIALIZED VIEW mv AS SELECT * FROM mv). The check fires when the same materialized view is entered a second time during Visitor.processTable traversal while stitching is enabled.","commonSituations":"Accidental self-reference when a view name shadows a base table name; re-creating a materialized view with a definition copied from the old version that referenced the old view; incremental refactorings where a base table was replaced by a materialized view of the same name, closing a cycle.","solutions":["Rewrite the materialized view definition to reference only base tables or non-recursive views/materialized views","If a name collision caused the cycle, rename the materialized view or qualify the base table reference explicitly (schema.table)","Break the dependency chain: drop the materialized view that loops back, or build an intermediate materialized view that does not cycle","Use DROP MATERIALIZED VIEW and recreate with an acyclic definition"],"exampleFix":"// before\nCREATE MATERIALIZED VIEW sales_daily AS SELECT * FROM sales_daily;\n// after\nCREATE MATERIALIZED VIEW sales_daily AS SELECT date, sum(amount) FROM sales GROUP BY date;","handlingStrategy":"validation","validationCode":"-- before creating, ensure the definition does not reference any materialized view that (transitively) references this one\n-- check references:\nSELECT * FROM system.metadata.materialized_views WHERE name = 'sales_daily';\n-- rewrite definition to use base tables only","typeGuard":null,"tryCatchPattern":"try {\n    session.execute(createMaterializedViewSql);\n} catch (SemanticException e) {\n    if (e.getCode() == MATERIALIZED_VIEW_IS_RECURSIVE) {\n        throw new IllegalStateException(\"MV definition is cyclic; rewrite to reference base tables\");\n    }\n    throw e;\n}","preventionTips":["Never reference the materialized view's own name inside its definition","Keep a dependency graph of views/MVs and validate it is acyclic before deploying DDL","Use fully qualified base-table names to avoid shadowing collisions","Review MV definitions after renaming base tables to views"],"tags":["sql","materialized-view","recursion","semantic-analysis"],"backgroundTag":"recursive-view-definition","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}