|
| 1 | +:sectnums: |
| 2 | +:sectnumlevels: 5 |
| 3 | + |
| 4 | +:imagesdir: ./_images |
| 5 | + |
| 6 | += Force View |
| 7 | + |
| 8 | +== Purpose |
| 9 | + |
| 10 | +- Force View offers Oracle-compatible `CREATE [OR REPLACE] FORCE VIEW` and `ALTER VIEW ... COMPILE` behavior, 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. |
| 11 | +- 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. |
| 12 | + |
| 13 | +== Implementation Description |
| 14 | + |
| 15 | +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. |
| 16 | + |
| 17 | +=== Grammar and Parsing Entry Points |
| 18 | + |
| 19 | +==== Force View Syntax Support |
| 20 | + |
| 21 | +- Register the Force View keywords in `src/backend/oracle_parser/ora_gram.y`. |
| 22 | +- Set `ViewStmt->force` during the grammar phase, and generate the `AT_ForceViewCompile` option when parsing `AlterTableStmt`. |
| 23 | +- Preserve `ViewStmt->stmt_literal` for later reuse when the placeholder is materialized. |
| 24 | + |
| 25 | +``` |
| 26 | +/* Insert or update the pg_force_view catalog as needed */ |
| 27 | +if (need_store) |
| 28 | +{ |
| 29 | + ...... |
| 30 | + |
| 31 | + StoreForceViewQuery(address.objectId, stmt->replace, ident_case, stmt->stmt_literal ? stmt->stmt_literal : queryString); |
| 32 | +} |
| 33 | +``` |
| 34 | + |
| 35 | +==== AST Field Extensions |
| 36 | + |
| 37 | +- Add `bool force` and `char *stmt_literal` to `ViewStmt` in `src/include/nodes/parsenodes.h`. |
| 38 | +- Define `AT_ForceViewCompile` consistently between `parsenodes.h` and `tablecmds.c` so that `ALTER VIEW ... COMPILE` flows into the regular alter-table machinery. |
| 39 | +- `parse_analyze` still follows the native path; Force mode intervenes only after a parsing error occurs. |
| 40 | + |
| 41 | +==== Parsing Entry |
| 42 | + |
| 43 | +- `DefineView()` in `src/backend/commands/view.c` now handles both normal and Force views. |
| 44 | +- The function first attempts normal parsing inside a `PG_TRY/PG_CATCH`; if the semantic analysis fails, it checks `stmt->force` to decide whether to enter the Force View branch. |
| 45 | +- On success it continues to `StoreViewQuery()`; on failure it falls back to the Force View logic so that the view object still exists. |
| 46 | + |
| 47 | +=== Force View Metadata |
| 48 | + |
| 49 | +==== `pg_force_view` Catalog |
| 50 | + |
| 51 | +- `src/include/catalog/pg_force_view.h` defines the catalog table, whose fields include the view OID (`fvoid`), identifier case (`ident_case`), and the original SQL text (`source`). |
| 52 | +- The unique index `pg_force_view_fvoid_index` plus the syscache `FORCEVIEWOID` (declared in `src/include/catalog/syscache_info.h`) enable lookups by view OID. |
| 53 | +- The catalog enables TOAST so lengthy SQL definitions are preserved in full, covering complex migration scripts. |
| 54 | + |
| 55 | +``` |
| 56 | +CATALOG(pg_force_view,9120,ForceViewRelationId) |
| 57 | +{ |
| 58 | + /* oid of force view */ |
| 59 | + Oid fvoid; |
| 60 | + |
| 61 | + /* see IDENT_CASE__xxx constants below */ |
| 62 | + char ident_case; |
| 63 | + |
| 64 | +#ifdef CATALOG_VARLEN /* variable-length fields start here */ |
| 65 | + |
| 66 | + /* sql definition */ |
| 67 | + text source; |
| 68 | +#endif |
| 69 | +} FormData_pg_force_view; |
| 70 | +``` |
| 71 | + |
| 72 | +==== View State |
| 73 | + |
| 74 | +- `bool rel_is_force_view(Oid relid)` in `src/backend/commands/view.c` determines whether a view is in Force state by checking whether the `_RETURN` rule exists. |
| 75 | +- Force views still register as `relkind = RELKIND_VIEW`, avoiding extra compatibility branches. |
| 76 | +- `pg_class.relhasrules` is set to `false` while in placeholder mode, which becomes part of the detection logic. |
| 77 | + |
| 78 | +=== Creation and Replacement Flow |
| 79 | + |
| 80 | +==== Normal View |
| 81 | + |
| 82 | +- After successful parsing, `DefineView()` calls `DefineVirtualRelation()` to populate `pg_class`, `pg_attribute`, and related catalogs. |
| 83 | +- `StoreViewQuery()` generates the `_RETURN` rule and records dependencies. |
| 84 | +- This path never touches `pg_force_view`; the view is immediately ready for use. |
| 85 | + |
| 86 | +==== Force View |
| 87 | + |
| 88 | +- `CreateForceVirtualPlaceholder()` in `src/backend/commands/view.c` creates or reuses a placeholder view: |
| 89 | + - If the view does not exist, it calls `DefineVirtualRelation()` to create the base object without a `_RETURN` rule. |
| 90 | + - If a Force View already exists, it reuses the current record and updates column definitions or cleans up legacy metadata. |
| 91 | + - If a normal view exists and `OR REPLACE` is specified, it invokes `make_view_invalid()` to invalidate the old definition before installing the placeholder. |
| 92 | +- `StoreForceViewQuery()` persists `stmt_literal` and the current `ivorysql.identifier_case_switch` to `pg_force_view` so identifier case semantics can be restored later. |
| 93 | +- After the placeholder is created, the client receives `WARNING: View created with compilation errors`, indicating the view cannot yet be used. |
| 94 | + |
| 95 | +=== Dependency Invalidations and Rollback |
| 96 | + |
| 97 | +==== Active Invalidation Logic |
| 98 | + |
| 99 | +- `make_view_invalid()` is triggered when dependencies are dropped, altered, or otherwise compromised. |
| 100 | +- The routine removes the `_RETURN` rule, clears `pg_depend` entries, resets `pg_class.relhasrules`, and truncates `pg_attribute` column metadata. |
| 101 | +- It also captures `CREATE FORCE VIEW ... AS <pg_get_viewdef>` and saves it in `pg_force_view`, while setting `ident_case` to `IDENT_CASE_UNDEFINE` to indicate the SQL is system-generated. |
| 102 | + |
| 103 | +==== Observable Behavior After Invalidation |
| 104 | + |
| 105 | +- The view remains visible in metadata catalogs but runtime access detects the Force flag. |
| 106 | +- Because `_RETURN` is missing, the executor calls `compile_force_view()` when opening the view; if compilation fails, it raises `view "<schema>.<name>" has errors`. |
| 107 | +- Users can recover by issuing `ALTER VIEW ... COMPILE` or `CREATE OR REPLACE FORCE VIEW` once dependencies are ready. |
| 108 | + |
| 109 | +=== Automatic and Manual Compilation |
| 110 | + |
| 111 | +==== Automatic Compilation Triggers |
| 112 | + |
| 113 | +- `addRangeTableEntry()` in `parse_relation.c` and the target-relation open logic in `parse_clause.c` call `compile_force_view()` after detecting a Force view. |
| 114 | +- The function reruns `raw_parser` and `parse_analyze`; on success it reinstalls the `_RETURN` rule, and on failure it aborts the statement with the encountered error. |
| 115 | + |
| 116 | +==== Manual Compilation |
| 117 | + |
| 118 | +- `AT_ForceViewCompile` executes during phase 2 in `tablecmds.c`, acquiring `AccessExclusiveLock` before invoking `compile_force_view()`. |
| 119 | +- Successful compilation behaves like a normal `ALTER VIEW`; failures emit `WARNING: View altered with compilation errors`, and the view stays in placeholder mode. |
| 120 | + |
| 121 | +==== Column Checks and Metadata Updates |
| 122 | + |
| 123 | +- `compile_force_view()` reads `pg_force_view.source`, rebuilds a `ViewStmt`, and calls `compile_force_view_internal()`. |
| 124 | +- The routine uses `checkViewColumns()` to compare legacy columns, allowing additions but rejecting incompatible type changes; new columns are applied through `AT_AddColumnToView()`. |
| 125 | +- `_RETURN` is regenerated via `StoreViewQuery()`, and `DeleteForceView()` removes the catalog record so the view becomes a standard one again. |
0 commit comments