{"record":{"id":"8f103dacabf7ae84","repo":"clockworklabs/SpacetimeDB","slug":"view-definition-cannot-read-from-other-views","errorCode":null,"errorMessage":"view definition cannot read from other views","messagePattern":"view definition cannot read from other views","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/core/src/host/wasm_common/module_host_actor.rs","lineNumber":197,"sourceCode":"\n    // Views bypass RLS, since views should enforce their own access control procedurally.\n    let auth = AuthCtx::for_current(database_identity);\n    let schema_view = SchemaViewer::new(&*tx, &auth);\n\n    // Compile to subscription plans.\n    let (plans, has_params) = SubscriptionPlan::compile(the_query, &schema_view, &auth)?;\n    ensure!(\n        !has_params,\n        \"parameterized SQL is not supported for view materialization yet\"\n    );\n\n    // Validate shape and disallow views-on-views.\n    for plan in &plans {\n        let Some(source_schema) = plan.return_table() else {\n            bail!(\"query does not return plain table rows\");\n        };\n        if plan.reads_from_view(true) || plan.reads_from_view(false) {\n            bail!(\"view definition cannot read from other views\");\n        }\n        if source_schema.row_type != *expected_row_type {\n            bail!(\n                \"query returns `{}` but view expects `{}`\",\n                fmt_algebraic_type(&AlgebraicType::Product(source_schema.row_type.clone())),\n                fmt_algebraic_type(&AlgebraicType::Product(expected_row_type.clone())),\n            );\n        }\n    }\n\n    let op = FuncCallType::View(call_info.clone());\n    let mut metrics = ExecutionMetrics::default();\n    let mut rows = Vec::new();\n\n    let params = ExecutionParams::from_auth(&auth);\n\n    for plan in plans {\n        // Track read sets for all tables involved in this plan.","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/9e0d92412ff2248f401a8ad12d535f2b5ac30912/crates/core/src/host/wasm_common/module_host_actor.rs#L179-L215","documentation":"View materialization only reads base tables. After compiling the view's SQL, the host checks each plan with reads_from_view(); any reference to another view -- even indirectly -- is rejected because views cannot be layered (it would require ordered/recursive materialization).","triggerScenarios":"CREATE VIEW v2 AS SELECT ... FROM v1 where v1 is itself a view; a view whose subquery selects from another view; refactoring a base table into a view and forgetting that dependent views now read a view.","commonSituations":"Building 'derived' views on top of convenience views for readability; incremental migrations that turn tables into views; copy-pasting view SQL that chains layers.","solutions":["Inline the underlying view's SQL: reference the base table(s) directly in the new view definition","Copy the filter/WHERE logic of the inner view into the outer definition","If composition is the goal, put the combined logic in one flat view or query it client-side via a subscription"],"exampleFix":"-- before: users_v is itself a view\nCREATE VIEW active_v AS SELECT * FROM users_v WHERE active;\n\n-- after: reference the base table instead\nCREATE VIEW active_v AS SELECT * FROM users WHERE active;","handlingStrategy":"validation","validationCode":"#!/usr/bin/env bash\n# naming views with a _v suffix makes view-on-view refs greppable\nif grep -rinE 'from[[:space:]]+[a-z0-9_]+_v\\b' sql/; then\n  echo 'possible view-on-view reference' >&2; exit 1\nfi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep views flat: always author them against base tables","Suffix view names (_v) so references are greppable in review","When turning a table into a view, sweep for dependent views immediately","Prefer one comprehensive view over chained convenience layers"],"tags":["sql","views","schema","materialization"],"backgroundTag":"sql-view-definition-invalid","analyzedSha":"9e0d92412ff2248f401a8ad12d535f2b5ac30912","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}