Force View
1. Purpose
-
Force View offers Oracle-compatible
CREATE [OR REPLACE] FORCE VIEWandALTER VIEW … COMPILEbehavior, allowing developers to persist a placeholder view even when dependencies are missing and then switch it back to a normal view once dependencies are available. -
The system records the original SQL text and identifier case mode in a dedicated catalog entry so that automatic or manual recompilation can restore the view with Oracle-style warnings and error reporting.
2. Implementation Description
In PostgreSQL, a base table referenced by a view cannot be dropped unless CASCADE is used. Oracle, however, allows the base table to be dropped while retaining the Force View and marking it as invalid. To align with Oracle semantics, IvorySQL introduces new grammar rules and a catalog that stores Force View metadata so that the Force state can be shared across sessions.
2.1. Grammar and Parsing Entry Points
2.1.1. Force View Syntax Support
-
Register the Force View keywords in
src/backend/oracle_parser/ora_gram.y. -
Set
ViewStmt→forceduring the grammar phase, and generate theAT_ForceViewCompileoption when parsingAlterTableStmt. -
Preserve
ViewStmt→stmt_literalfor later reuse when the placeholder is materialized.
/* Insert or update the pg_force_view catalog as needed */
if (need_store)
{
......
StoreForceViewQuery(address.objectId, stmt->replace, ident_case, stmt->stmt_literal ? stmt->stmt_literal : queryString);
}
2.1.2. AST Field Extensions
-
Add
bool forceandchar *stmt_literaltoViewStmtinsrc/include/nodes/parsenodes.h. -
Define
AT_ForceViewCompileconsistently betweenparsenodes.handtablecmds.cso thatALTER VIEW … COMPILEflows into the regular alter-table machinery. -
parse_analyzestill follows the native path; Force mode intervenes only after a parsing error occurs.
2.1.3. Parsing Entry
-
DefineView()insrc/backend/commands/view.cnow handles both normal and Force views. -
The function first attempts normal parsing inside a
PG_TRY/PG_CATCH; if the semantic analysis fails, it checksstmt→forceto decide whether to enter the Force View branch. -
On success it continues to
StoreViewQuery(); on failure it falls back to the Force View logic so that the view object still exists.
2.2. Force View Metadata
2.2.1. pg_force_view Catalog
-
src/include/catalog/pg_force_view.hdefines the catalog table, whose fields include the view OID (fvoid), identifier case (ident_case), and the original SQL text (source). -
The unique index
pg_force_view_fvoid_indexplus the syscacheFORCEVIEWOID(declared insrc/include/catalog/syscache_info.h) enable lookups by view OID. -
The catalog enables TOAST so lengthy SQL definitions are preserved in full, covering complex migration scripts.
CATALOG(pg_force_view,9120,ForceViewRelationId)
{
/* oid of force view */
Oid fvoid;
/* see IDENT_CASE__xxx constants below */
char ident_case;
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/* sql definition */
text source;
#endif
} FormData_pg_force_view;
2.2.2. View State
-
bool rel_is_force_view(Oid relid)insrc/backend/commands/view.cdetermines whether a view is in Force state by checking whether the_RETURNrule exists. -
Force views still register as
relkind = RELKIND_VIEW, avoiding extra compatibility branches. -
pg_class.relhasrulesis set tofalsewhile in placeholder mode, which becomes part of the detection logic.
2.3. Creation and Replacement Flow
2.3.1. Normal View
-
After successful parsing,
DefineView()callsDefineVirtualRelation()to populatepg_class,pg_attribute, and related catalogs. -
StoreViewQuery()generates the_RETURNrule and records dependencies. -
This path never touches
pg_force_view; the view is immediately ready for use.
2.3.2. Force View
-
CreateForceVirtualPlaceholder()insrc/backend/commands/view.ccreates or reuses a placeholder view: -
If the view does not exist, it calls
DefineVirtualRelation()to create the base object without a_RETURNrule. -
If a Force View already exists, it reuses the current record and updates column definitions or cleans up legacy metadata.
-
If a normal view exists and
OR REPLACEis specified, it invokesmake_view_invalid()to invalidate the old definition before installing the placeholder. -
StoreForceViewQuery()persistsstmt_literaland the currentivorysql.identifier_case_switchtopg_force_viewso identifier case semantics can be restored later. -
After the placeholder is created, the client receives
WARNING: View created with compilation errors, indicating the view cannot yet be used.
2.4. Dependency Invalidations and Rollback
2.4.1. Active Invalidation Logic
-
make_view_invalid()is triggered when dependencies are dropped, altered, or otherwise compromised. -
The routine removes the
_RETURNrule, clearspg_dependentries, resetspg_class.relhasrules, and truncatespg_attributecolumn metadata. -
It also captures
CREATE FORCE VIEW … AS <pg_get_viewdef>and saves it inpg_force_view, while settingident_casetoIDENT_CASE_UNDEFINEto indicate the SQL is system-generated.
2.4.2. Observable Behavior After Invalidation
-
The view remains visible in metadata catalogs but runtime access detects the Force flag.
-
Because
_RETURNis missing, the executor callscompile_force_view()when opening the view; if compilation fails, it raisesview "<schema>.<name>" has errors. -
Users can recover by issuing
ALTER VIEW … COMPILEorCREATE OR REPLACE FORCE VIEWonce dependencies are ready.
2.5. Automatic and Manual Compilation
2.5.1. Automatic Compilation Triggers
-
addRangeTableEntry()inparse_relation.cand the target-relation open logic inparse_clause.ccallcompile_force_view()after detecting a Force view. -
The function reruns
raw_parserandparse_analyze; on success it reinstalls the_RETURNrule, and on failure it aborts the statement with the encountered error.
2.5.2. Manual Compilation
-
AT_ForceViewCompileexecutes during phase 2 intablecmds.c, acquiringAccessExclusiveLockbefore invokingcompile_force_view(). -
Successful compilation behaves like a normal
ALTER VIEW; failures emitWARNING: View altered with compilation errors, and the view stays in placeholder mode.
2.5.3. Column Checks and Metadata Updates
-
compile_force_view()readspg_force_view.source, rebuilds aViewStmt, and callscompile_force_view_internal(). -
The routine uses
checkViewColumns()to compare legacy columns, allowing additions but rejecting incompatible type changes; new columns are applied throughAT_AddColumnToView(). -
_RETURNis regenerated viaStoreViewQuery(), andDeleteForceView()removes the catalog record so the view becomes a standard one again.