Rename columns in old-style continuous aggregates

For continuous aggregates with the old-style partial aggregates
renaming columns that are not in the group-by clause will generate an
error when upgrading to a later version. The reason is that it is
implicitly assumed that the name of the column is the same as for the
direct view. This holds true for new-style continous aggregates, but is
not always true for old-style continuous aggregates. In particular,
columns that are not part of the `GROUP BY` clause can have an
internally generated name.

This commit fixes that by extracting the name of the column from the
partial view and use that when renaming the partial view column and the
materialized table column.
This commit is contained in:
Mats Kindahl 2023-02-28 12:04:17 +01:00 committed by Mats Kindahl
parent e2e7ae3045
commit a6ff7ba6cc
3 changed files with 45 additions and 17 deletions

View File

@ -11,9 +11,12 @@ accidentally triggering the load of a previous DB version.**
* #5362 Make copy fetcher more async
* #5336 Use NameData and namestrcpy for names
* #5317 Fix some incorrect memory handling
* #5367 Rename columns in old-style continuous aggregates
**Thanks**
* @Medvecrab for discovering an issue with copying NameData when forming heap tuples.
* @pushpeepkmonroe for discovering an issue in upgrading old-style
continuous aggregates with renamed columns
## 2.10.0 (2023-02-21)

View File

@ -6,10 +6,11 @@ DROP FUNCTION IF EXISTS _timescaledb_internal.time_col_type_for_chunk(name,name)
-- in a table.
CREATE TABLE _timescaledb_internal.rename_tables (
user_view regclass,
new_name text,
old_name text,
user_column text,
partial_view regclass,
partial_column text,
direct_view regclass,
direct_column text,
mat_table regclass,
hypertable_id int
);
@ -31,24 +32,32 @@ WITH
SELECT attrelid, attname, attnum, mat_id
FROM objs, pg_attribute
WHERE attrelid = objs.user_view),
partial_view AS (
SELECT attrelid, attname, attnum, mat_id
FROM objs, pg_attribute
WHERE attrelid = objs.partial_view),
direct_view AS (
SELECT attrelid, attname, attnum, mat_id
FROM objs, pg_attribute
WHERE attrelid = objs.direct_view)
INSERT INTO _timescaledb_internal.rename_tables
SELECT (SELECT user_view FROM objs WHERE uv.attrelid = user_view),
uv.attname AS new_name,
dv.attname AS old_name,
uv.attname AS user_column,
(SELECT partial_view FROM objs WHERE uv.attrelid = user_view),
pv.attname AS partial_column,
(SELECT direct_view FROM objs WHERE uv.attrelid = user_view),
dv.attname AS direct_column,
(SELECT mat_table FROM objs WHERE uv.attrelid = user_view),
(SELECT mat_id FROM objs WHERE uv.attrelid = user_view)
FROM user_view uv JOIN direct_view dv USING (mat_id, attnum)
JOIN partial_view pv USING (mat_id, attnum)
WHERE uv.attname != dv.attname;
CREATE PROCEDURE _timescaledb_internal.alter_table_column(cagg regclass, relation regclass, old_column_name name, new_column_name name) AS $$
BEGIN
EXECUTE format('ALTER TABLE %s RENAME COLUMN %I TO %I', relation, old_column_name, new_column_name);
IF old_column_name != new_column_name THEN
EXECUTE format('ALTER TABLE %s RENAME COLUMN %I TO %I', relation, old_column_name, new_column_name);
END IF;
END;
$$ LANGUAGE plpgsql;
@ -59,23 +68,24 @@ DO
$$
DECLARE
user_view regclass;
new_name name;
old_name name;
user_column name;
partial_view regclass;
partial_column name;
direct_view regclass;
direct_column name;
mat_table regclass;
ht_id int;
BEGIN
FOR user_view, new_name, old_name, partial_view, direct_view, mat_table, ht_id IN
FOR user_view, user_column, partial_view, partial_column, direct_view, direct_column, mat_table, ht_id IN
SELECT * FROM _timescaledb_internal.rename_tables
LOOP
-- There is no RENAME COLUMN for views, but we can use ALTER TABLE
-- to rename a column in a view.
CALL _timescaledb_internal.alter_table_column(user_view, partial_view, old_name, new_name);
CALL _timescaledb_internal.alter_table_column(user_view, direct_view, old_name, new_name);
CALL _timescaledb_internal.alter_table_column(user_view, mat_table, old_name, new_name);
UPDATE _timescaledb_catalog.dimension SET column_name = new_name
WHERE hypertable_id = ht_id AND column_name = old_name;
CALL _timescaledb_internal.alter_table_column(user_view, partial_view, partial_column, user_column);
CALL _timescaledb_internal.alter_table_column(user_view, direct_view, direct_column, user_column);
CALL _timescaledb_internal.alter_table_column(user_view, mat_table, partial_column, user_column);
UPDATE _timescaledb_catalog.dimension SET column_name = user_column
WHERE hypertable_id = ht_id AND column_name = direct_column;
END LOOP;
END
$$;

View File

@ -21,7 +21,8 @@ SELECT
:ts_major >= 2 AS has_create_mat_view,
:ts_major >= 2 AS has_continuous_aggs_policy,
:ts_major = 2 AND :ts_minor >= 7 AS has_continuous_aggs_finals_form,
:ts_major = 2 AND :ts_minor IN (7,8) AS has_continuous_aggs_finalized_option
:ts_major = 2 AND :ts_minor IN (7,8) AS has_continuous_aggs_finalized_option,
:ts_major = 2 AND :ts_minor IN (5) AS has_cagg_rename_col_bug
FROM pg_extension
WHERE extname = 'timescaledb' \gset
@ -69,7 +70,9 @@ SELECT generate_series('2018-11-01 00:00'::timestamp, '2018-12-15 00:00'::timest
WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS
\endif
\endif
SELECT time_bucket('1 week', timec) AS bucket, location, round(avg(humidity)) AS humidity
SELECT time_bucket('1 week', timec) AS bucket,
location,
round(avg(humidity)) AS humidity
FROM conditions_before
\if :has_refresh_mat_view
GROUP BY bucket, location;
@ -132,15 +135,27 @@ SELECT generate_series('2018-11-01 00:00'::timestamp, '2018-12-15 00:00'::timest
\if :has_refresh_mat_view
GROUP BY bucket, location
HAVING min(location) >= 'NYC' and avg(temperature) > 2;
-- ALTER VIEW cannot rename columns before PG13, but ALTER TABLE
-- works for views.
ALTER TABLE rename_cols RENAME COLUMN bucket to "time";
--
-- Renaming one column that is used in the group by and one that is
-- not in the group by. Both should work.
ALTER TABLE rename_cols RENAME COLUMN bucket TO "time";
\if :has_cagg_rename_col_bug == false
ALTER TABLE rename_cols RENAME COLUMN humidity TO moisture;
\endif
\else
GROUP BY bucket, location
HAVING min(location) >= 'NYC' and avg(temperature) > 2 WITH NO DATA;
SELECT add_continuous_aggregate_policy('mat_before', NULL, '-30 days'::interval, '336 h');
ALTER MATERIALIZED VIEW rename_cols RENAME COLUMN bucket to "time";
-- Renaming one column that is used in the group by and one that
-- is not in the group by. Both should work.
ALTER MATERIALIZED VIEW rename_cols RENAME COLUMN bucket TO "time";
\if :has_cagg_rename_col_bug == false
ALTER MATERIALIZED VIEW rename_cols RENAME COLUMN humidity TO moisture;
\endif
\endif
\if :WITH_SUPERUSER