|
| 1 | +CREATE OR REPLACE FUNCTION @ [email protected]_template_properties (p_parent_table text, p_child_schema text, p_child_tablename text) RETURNS boolean |
| 2 | + LANGUAGE plpgsql |
| 3 | + AS $$ |
| 4 | +DECLARE |
| 5 | + |
| 6 | +v_child_relkind char; |
| 7 | +v_child_schema text; |
| 8 | +v_child_tablename text; |
| 9 | +v_child_unlogged char; |
| 10 | +v_dupe_found boolean := false; |
| 11 | +v_fk_list record; |
| 12 | +v_index_list record; |
| 13 | +v_inherit_fk boolean; |
| 14 | +v_parent_index_list record; |
| 15 | +v_parent_oid oid; |
| 16 | +v_parent_table text; |
| 17 | +v_sql text; |
| 18 | +v_template_oid oid; |
| 19 | +v_template_schemaname text; |
| 20 | +v_template_table text; |
| 21 | +v_template_tablename name; |
| 22 | +v_template_tablespace name; |
| 23 | +v_template_unlogged char; |
| 24 | + |
| 25 | +BEGIN |
| 26 | +/* |
| 27 | + * Function to inherit the properties of the template table to newly created child tables. |
| 28 | + * Currently used for PostgreSQL 10 to inherit indexes and FKs since that is not natively available |
| 29 | + * For PG11, used to inherit non-partition-key unique indexes & primary keys |
| 30 | + */ |
| 31 | + |
| 32 | +SELECT parent_table, template_table, inherit_fk |
| 33 | +INTO v_parent_table, v_template_table, v_inherit_fk |
| 34 | + |
| 35 | +WHERE parent_table = p_parent_table; |
| 36 | +IF v_parent_table IS NULL THEN |
| 37 | + RAISE EXCEPTION 'Given parent table has no configuration in pg_partman: %', p_parent_table; |
| 38 | +ELSIF v_template_table IS NULL THEN |
| 39 | + RAISE EXCEPTION 'No template table set in configuration for given parent table: %', p_parent_table; |
| 40 | +END IF; |
| 41 | + |
| 42 | +SELECT c.oid INTO v_parent_oid |
| 43 | +FROM pg_catalog.pg_class c |
| 44 | +JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid |
| 45 | +WHERE n.nspname = split_part(p_parent_table, '.', 1)::name |
| 46 | +AND c.relname = split_part(p_parent_table, '.', 2)::name; |
| 47 | + IF v_parent_oid IS NULL THEN |
| 48 | + RAISE EXCEPTION 'Unable to find given parent table in system catalogs: %', p_parent_table; |
| 49 | + END IF; |
| 50 | + |
| 51 | +SELECT n.nspname, c.relname, c.relkind INTO v_child_schema, v_child_tablename, v_child_relkind |
| 52 | +FROM pg_catalog.pg_class c |
| 53 | +JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid |
| 54 | +WHERE n.nspname = p_child_schema::name |
| 55 | +AND c.relname = p_child_tablename::name; |
| 56 | + IF v_child_tablename IS NULL THEN |
| 57 | + RAISE EXCEPTION 'Unable to find given child table in system catalogs: %.%', v_child_schema, v_child_tablename; |
| 58 | + END IF; |
| 59 | + |
| 60 | +IF v_child_relkind = 'p' THEN |
| 61 | + -- Subpartitioned parent, do not apply properties |
| 62 | + RAISE DEBUG 'inherit_template_properties: found given child is subpartition parent, so properties not inherited'; |
| 63 | + RETURN false; |
| 64 | +END IF; |
| 65 | + |
| 66 | +v_template_schemaname := split_part(v_template_table, '.', 1)::name; |
| 67 | +v_template_tablename := split_part(v_template_table, '.', 2)::name; |
| 68 | + |
| 69 | + SELECT c.oid, ts.spcname INTO v_template_oid, v_template_tablespace |
| 70 | +FROM pg_catalog.pg_class c |
| 71 | +JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid |
| 72 | +LEFT OUTER JOIN pg_catalog.pg_tablespace ts ON c.reltablespace = ts.oid |
| 73 | +WHERE n.nspname = v_template_schemaname |
| 74 | +AND c.relname = v_template_tablename; |
| 75 | + IF v_template_oid IS NULL THEN |
| 76 | + RAISE EXCEPTION 'Unable to find configured template table in system catalogs: %', v_template_table; |
| 77 | + END IF; |
| 78 | + |
| 79 | +-- Index creation (Required for all indexes in PG10. Only for non-unique, non-partition key indexes in PG11) |
| 80 | +IF current_setting('server_version_num')::int >= 100000 THEN |
| 81 | + FOR v_index_list IN |
| 82 | + SELECT |
| 83 | + array_to_string(regexp_matches(pg_get_indexdef(indexrelid), ' USING .*'),',') AS statement |
| 84 | + , i.indisprimary |
| 85 | + , i.indisunique |
| 86 | + , ( SELECT array_agg( a.attname ORDER by x.r ) |
| 87 | + FROM pg_catalog.pg_attribute a |
| 88 | + JOIN ( SELECT k, row_number() over () as r |
| 89 | + FROM unnest(i.indkey) k ) as x |
| 90 | + ON a.attnum = x.k AND a.attrelid = i.indrelid |
| 91 | + ) AS indkey_names |
| 92 | + , c.relname AS index_name |
| 93 | + , ts.spcname AS tablespace_name |
| 94 | + FROM pg_catalog.pg_index i |
| 95 | + JOIN pg_catalog.pg_class c ON i.indexrelid = c.oid |
| 96 | + LEFT OUTER JOIN pg_catalog.pg_tablespace ts ON c.reltablespace = ts.oid |
| 97 | + WHERE i.indrelid = v_template_oid |
| 98 | + AND i.indisvalid |
| 99 | + ORDER BY 1 |
| 100 | + LOOP |
| 101 | + v_dupe_found := false; |
| 102 | + |
| 103 | + IF current_setting('server_version_num')::int >= 110000 THEN |
| 104 | + FOR v_parent_index_list IN |
| 105 | + SELECT |
| 106 | + array_to_string(regexp_matches(pg_get_indexdef(indexrelid), ' USING .*'),',') AS statement |
| 107 | + , i.indisprimary |
| 108 | + , ( SELECT array_agg( a.attname ORDER by x.r ) |
| 109 | + FROM pg_catalog.pg_attribute a |
| 110 | + JOIN ( SELECT k, row_number() over () as r |
| 111 | + FROM unnest(i.indkey) k ) as x |
| 112 | + ON a.attnum = x.k AND a.attrelid = i.indrelid |
| 113 | + ) AS indkey_names |
| 114 | + FROM pg_catalog.pg_index i |
| 115 | + WHERE i.indrelid = v_parent_oid |
| 116 | + AND i.indisvalid |
| 117 | + ORDER BY 1 |
| 118 | + LOOP |
| 119 | + |
| 120 | + IF v_parent_index_list.indisprimary AND v_index_list.indisprimary THEN |
| 121 | + IF v_parent_index_list.indkey_names = v_index_list.indkey_names THEN |
| 122 | + RAISE DEBUG 'Ignoring duplicate primary key on template table: % ', v_index_list.indkey_names; |
| 123 | + v_dupe_found := true; |
| 124 | + CONTINUE; -- only continue within this nested loop |
| 125 | + END IF; |
| 126 | + END IF; |
| 127 | + |
| 128 | + IF v_parent_index_list.statement = v_index_list.statement THEN |
| 129 | + RAISE DEBUG 'Ignoring duplicate index on template table: %', v_index_list.statement; |
| 130 | + v_dupe_found := true; |
| 131 | + CONTINUE; -- only continue within this nested loop |
| 132 | + END IF; |
| 133 | + |
| 134 | + END LOOP; -- end parent index loop |
| 135 | + END IF; -- End PG11 check |
| 136 | + |
| 137 | + IF v_dupe_found = true THEN |
| 138 | + -- Only used in PG11 and should skip trying to create indexes that already existed on the parent |
| 139 | + CONTINUE; |
| 140 | + END IF; |
| 141 | + |
| 142 | + IF v_index_list.indisprimary THEN |
| 143 | + v_sql := format('ALTER TABLE %I.%I ADD PRIMARY KEY (%s)' |
| 144 | + , v_child_schema |
| 145 | + , v_child_tablename |
| 146 | + , '"' || array_to_string(v_index_list.indkey_names, '","') || '"'); |
| 147 | + IF v_index_list.tablespace_name IS NOT NULL THEN |
| 148 | + v_sql := v_sql || format(' USING INDEX TABLESPACE %I', v_index_list.tablespace_name); |
| 149 | + END IF; |
| 150 | + RAISE DEBUG 'Create pk: %', v_sql; |
| 151 | + EXECUTE v_sql; |
| 152 | + ELSE |
| 153 | + -- statement column should be just the portion of the index definition that defines what it actually is |
| 154 | + v_sql := format('CREATE %s INDEX ON %I.%I %s', CASE WHEN v_index_list.indisunique = TRUE THEN 'UNIQUE' ELSE '' END, v_child_schema, v_child_tablename, v_index_list.statement); |
| 155 | + IF v_index_list.tablespace_name IS NOT NULL THEN |
| 156 | + v_sql := v_sql || format(' TABLESPACE %I', v_index_list.tablespace_name); |
| 157 | + END IF; |
| 158 | + |
| 159 | + RAISE DEBUG 'Create index: %', v_sql; |
| 160 | + EXECUTE v_sql; |
| 161 | + |
| 162 | + END IF; |
| 163 | + |
| 164 | + END LOOP; |
| 165 | +END IF; |
| 166 | +-- End index creation |
| 167 | + |
| 168 | +-- Foreign key creation (PG10 only) |
| 169 | +IF current_setting('server_version_num')::int >= 100000 AND current_setting('server_version_num')::int < 110000 THEN |
| 170 | + IF v_inherit_fk THEN |
| 171 | + FOR v_fk_list IN |
| 172 | + SELECT pg_get_constraintdef(con.oid) AS constraint_def |
| 173 | + FROM pg_catalog.pg_constraint con |
| 174 | + JOIN pg_catalog.pg_class c ON con.conrelid = c.oid |
| 175 | + WHERE c.oid = v_template_oid |
| 176 | + AND contype = 'f' |
| 177 | + LOOP |
| 178 | + v_sql := format('ALTER TABLE %I.%I ADD %s', v_child_schema, v_child_tablename, v_fk_list.constraint_def); |
| 179 | + RAISE DEBUG 'Create FK: %', v_sql; |
| 180 | + EXECUTE v_sql; |
| 181 | + END LOOP; |
| 182 | + END IF; |
| 183 | +END IF; |
| 184 | +-- End foreign key creation |
| 185 | + |
| 186 | +-- Tablespace inheritance |
| 187 | +IF v_template_tablespace IS NOT NULL THEN |
| 188 | + v_sql := format('ALTER TABLE %I.%I SET TABLESPACE %I', v_child_schema, v_child_tablename, v_template_tablespace); |
| 189 | + RAISE DEBUG 'Alter tablespace: %', v_sql; |
| 190 | + EXECUTE v_sql; |
| 191 | +END IF; |
| 192 | + |
| 193 | +-- UNLOGGED status. Currently waiting on final stance of how native will handle this property being changed for its children. |
| 194 | +-- See release notes for v4.2.0 |
| 195 | +SELECT relpersistence INTO v_template_unlogged |
| 196 | +FROM pg_catalog.pg_class c |
| 197 | +JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid |
| 198 | +WHERE n.nspname = v_template_schemaname |
| 199 | +AND c.relname = v_template_tablename; |
| 200 | + |
| 201 | +SELECT relpersistence INTO v_child_unlogged |
| 202 | +FROM pg_catalog.pg_class c |
| 203 | +JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid |
| 204 | +WHERE n.nspname = v_child_schema::name |
| 205 | +AND c.relname = v_child_tablename::name; |
| 206 | + |
| 207 | +IF v_template_unlogged = 'u' AND v_child_unlogged = 'p' THEN |
| 208 | + v_sql := format ('ALTER TABLE %I.%I SET UNLOGGED', v_child_schema, v_child_tablename); |
| 209 | + RAISE DEBUG 'Alter UNLOGGED: %', v_sql; |
| 210 | + EXECUTE v_sql; |
| 211 | +ELSIF v_template_unlogged = 'p' AND v_child_unlogged = 'u' THEN |
| 212 | + v_sql := format ('ALTER TABLE %I.%I SET LOGGED', v_child_schema, v_child_tablename); |
| 213 | + RAISE DEBUG 'Alter UNLOGGED: %', v_sql; |
| 214 | + EXECUTE v_sql; |
| 215 | +END IF; |
| 216 | + |
| 217 | +RETURN true; |
| 218 | + |
| 219 | +END |
| 220 | +$$; |
| 221 | + |
0 commit comments