|
| 1 | +:sectnums: |
| 2 | +:sectnumlevels: 5 |
| 3 | + |
| 4 | +:imagesdir: ./_images |
| 5 | + |
| 6 | += Rebuild View After Column Type Changed |
| 7 | + |
| 8 | +== Feature Overview |
| 9 | + |
| 10 | +In standard PostgreSQL, if a column is referenced by a view, attempting to change its data type via `ALTER TABLE ... ALTER COLUMN ... TYPE` results |
| 11 | + in an immediate error: |
| 12 | + |
| 13 | +---- |
| 14 | +ERROR: cannot alter type of a column used by a view or rule |
| 15 | +---- |
| 16 | + |
| 17 | +Users are required to manually drop all dependent views, perform the column type change, and then recreate each view one by one — a tedious and |
| 18 | +error-prone process that becomes particularly difficult when multiple levels of cascaded view dependencies exist. |
| 19 | + |
| 20 | +IvorySQL enhances this behavior: when a column type change is executed, the database automatically saves the definitions of all dependent views |
| 21 | +(including indirectly dependent cascaded views), and after completing the type change, rebuilds those views in the correct dependency order — |
| 22 | +entirely transparent to the user. If an error occurs during rebuilding (for example, a view uses an operator not supported by the new type), the |
| 23 | +entire `ALTER TABLE` operation is rolled back, ensuring data consistency. |
| 24 | + |
| 25 | +This feature is supported in both PG-compatible mode and Oracle-compatible mode. |
| 26 | + |
| 27 | +== Syntax |
| 28 | + |
| 29 | +The syntax is identical to the standard `ALTER TABLE` — no additional keywords are required: |
| 30 | + |
| 31 | +[source,sql] |
| 32 | +---- |
| 33 | +ALTER TABLE table_name ALTER COLUMN column_name TYPE new_type; |
| 34 | +---- |
| 35 | + |
| 36 | +Parameter description: |
| 37 | + |
| 38 | +- `table_name`: The target table name, optionally schema-qualified; |
| 39 | +- `column_name`: The name of the column whose type is to be changed; |
| 40 | +- `new_type`: The target data type, which must be compatible with the original type or implicitly castable. |
| 41 | + |
| 42 | +== Test Cases |
| 43 | + |
| 44 | +=== Single View Dependency: Automatic Rebuild |
| 45 | + |
| 46 | +[source,sql] |
| 47 | +---- |
| 48 | +-- Create the base table |
| 49 | +CREATE TABLE t (a int, b text); |
| 50 | + |
| 51 | +-- Create a view that references column a |
| 52 | +CREATE VIEW v AS SELECT a, b FROM t; |
| 53 | +
|
| 54 | +-- Standard PostgreSQL would error here; IvorySQL rebuilds the view automatically |
| 55 | +ALTER TABLE t ALTER COLUMN a TYPE bigint; |
| 56 | +
|
| 57 | +-- Verify the view is still valid and the column type has been updated |
| 58 | +SELECT pg_typeof(a) FROM v LIMIT 1; |
| 59 | +-- Returns: bigint |
| 60 | + |
| 61 | +\d v |
| 62 | + View "public.v" |
| 63 | + Column | Type | Collation | Nullable | Default |
| 64 | +--------+--------+-----------+----------+--------- |
| 65 | + a | bigint | | | |
| 66 | + b | text | | | |
| 67 | +---- |
| 68 | + |
| 69 | +=== Cascaded View Dependencies: Ordered Automatic Rebuild |
| 70 | + |
| 71 | +[source,sql] |
| 72 | +---- |
| 73 | +-- Create the base table |
| 74 | +CREATE TABLE t (a int, b text); |
| 75 | +
|
| 76 | +-- Create two levels of view dependency: v2 depends on v1, v1 depends on t |
| 77 | +CREATE VIEW v1 AS SELECT a, b FROM t; |
| 78 | +CREATE VIEW v2 AS SELECT a FROM v1; |
| 79 | +
|
| 80 | +-- Change the column type; v1 and v2 are automatically rebuilt in dependency order |
| 81 | +ALTER TABLE t ALTER COLUMN a TYPE bigint; |
| 82 | +
|
| 83 | +-- Verify both views have been correctly rebuilt |
| 84 | +SELECT pg_typeof(a) FROM v1 LIMIT 1; |
| 85 | +-- Returns: bigint |
| 86 | + pg_typeof |
| 87 | +----------- |
| 88 | +(0 rows) |
| 89 | + |
| 90 | +SELECT pg_typeof(a) FROM v2 LIMIT 1; |
| 91 | +-- Returns: bigint |
| 92 | + pg_typeof |
| 93 | +----------- |
| 94 | +(0 rows) |
| 95 | +---- |
| 96 | + |
| 97 | +=== Preserving View Options: security_barrier |
| 98 | + |
| 99 | +[source,sql] |
| 100 | +---- |
| 101 | +-- Create a view with the security_barrier option |
| 102 | +CREATE VIEW v WITH (security_barrier) AS SELECT a, b FROM t; |
| 103 | +
|
| 104 | +ALTER TABLE t ALTER COLUMN a TYPE bigint; |
| 105 | +
|
| 106 | +-- Verify the security_barrier option is correctly preserved after rebuild |
| 107 | +SELECT relname, reloptions FROM pg_class WHERE relname = 'v'; |
| 108 | +-- reloptions: {security_barrier=true} |
| 109 | + relname | reloptions |
| 110 | +---------+------------------------- |
| 111 | + v | {security_barrier=true} |
| 112 | +(1 row) |
| 113 | +---- |
| 114 | + |
| 115 | +=== Preserving View Options: WITH CHECK OPTION |
| 116 | + |
| 117 | +[source,sql] |
| 118 | +---- |
| 119 | +-- Create a view with WITH LOCAL CHECK OPTION |
| 120 | +CREATE VIEW v AS SELECT a, b FROM t WHERE a > 0 |
| 121 | + WITH LOCAL CHECK OPTION; |
| 122 | +
|
| 123 | +ALTER TABLE t ALTER COLUMN a TYPE bigint; |
| 124 | +
|
| 125 | +-- Verify the CHECK OPTION is correctly preserved after rebuild |
| 126 | +\d+ v |
| 127 | + View "public.v" |
| 128 | + Column | Type | Collation | Nullable | Default | Storage | Description |
| 129 | +--------+--------+-----------+----------+---------+----------+------------- |
| 130 | + a | bigint | | | | plain | |
| 131 | + b | text | | | | extended | |
| 132 | +View definition: |
| 133 | + SELECT t.a, |
| 134 | + t.b |
| 135 | + FROM t |
| 136 | + WHERE t.a > 0; |
| 137 | +Options: check_option=local |
| 138 | +---- |
| 139 | + |
| 140 | +=== Full Rollback on Rebuild Failure |
| 141 | + |
| 142 | +[source,sql] |
| 143 | +---- |
| 144 | +CREATE TABLE t (a int, b text); |
| 145 | +CREATE VIEW v AS SELECT a::integer + 1 AS a_plus FROM t; |
| 146 | +
|
| 147 | +-- If the new type is incompatible with expressions in the view, the entire operation rolls back. |
| 148 | +-- For example, changing a to type text makes the expression a::integer + 1 invalid. |
| 149 | +ALTER TABLE t ALTER COLUMN a TYPE text; |
| 150 | +-- ERROR: operator does not exist: text + integer |
| 151 | +-- HINT: ... |
| 152 | +-- ROLLBACK |
| 153 | +
|
| 154 | +-- Confirm that the table structure and view are both unaffected |
| 155 | +\d t |
| 156 | + Table "public.t" |
| 157 | + Column | Type | Collation | Nullable | Default |
| 158 | +--------+---------+-----------+----------+--------- |
| 159 | + a | integer | | | |
| 160 | + b | text | | | |
| 161 | +---- |
| 162 | + |
| 163 | +== Limitations |
| 164 | + |
| 165 | +. If a view uses an operator or function incompatible with the new column type (e.g., arithmetic on a `text` column), the rebuild will fail and the |
| 166 | + entire `ALTER TABLE` operation will be rolled back; |
| 167 | +. Only **views** are automatically rebuilt; **rules** that depend on the affected column will still cause an error; |
| 168 | +. The column type change must satisfy PostgreSQL's type casting rules; arbitrary conversions between unrelated types are not supported; |
| 169 | +. Views are rebuilt in topological dependency order; circular dependencies (normally prevented by the database) are not handled; |
| 170 | +. The rebuild process runs within the same transaction, meaning dependent views are unavailable during the operation — this may affect concurrent |
| 171 | +queries in high-concurrency environments. |
0 commit comments