|
| 1 | + |
| 2 | +DROP PROCEDURE IF EXISTS _timescaledb_functions.repair_relation_acls(); |
| 3 | +DROP FUNCTION IF EXISTS _timescaledb_functions.makeaclitem(regrole, regrole, text, bool); |
| 4 | + |
| 5 | +-- Create watermark record when required. This uses pure SQL to avoid calling |
| 6 | +-- C functions that need catalog access during ALTER EXTENSION UPDATE. |
| 7 | +-- this is only needed for users upgrading from before 2.11.0, as the watermark |
| 8 | +-- was added in that version. |
| 9 | +DO |
| 10 | +$$ |
| 11 | +DECLARE |
| 12 | + ts_version TEXT; |
| 13 | + cagg_rec RECORD; |
| 14 | + max_val BIGINT; |
| 15 | + watermark_val BIGINT; |
| 16 | + bucket_width_val BIGINT; |
| 17 | +BEGIN |
| 18 | + SELECT extversion INTO ts_version FROM pg_extension WHERE extname = 'timescaledb'; |
| 19 | + IF ts_version < '2.11.0' THEN |
| 20 | + RETURN; |
| 21 | + END IF; |
| 22 | + |
| 23 | + FOR cagg_rec IN |
| 24 | + SELECT a.mat_hypertable_id, |
| 25 | + h.schema_name, h.table_name, |
| 26 | + d.column_name, d.column_type, |
| 27 | + bf.bucket_width, bf.bucket_fixed_width |
| 28 | + FROM _timescaledb_catalog.continuous_agg a |
| 29 | + LEFT JOIN _timescaledb_catalog.continuous_aggs_watermark w ON w.mat_hypertable_id = a.mat_hypertable_id |
| 30 | + JOIN _timescaledb_catalog.hypertable h ON h.id = a.mat_hypertable_id |
| 31 | + JOIN _timescaledb_catalog.dimension d ON d.hypertable_id = a.mat_hypertable_id AND d.num_slices IS NULL |
| 32 | + LEFT JOIN _timescaledb_catalog.continuous_aggs_bucket_function bf ON bf.mat_hypertable_id = a.mat_hypertable_id |
| 33 | + WHERE w.mat_hypertable_id IS NULL |
| 34 | + ORDER BY a.mat_hypertable_id |
| 35 | + LOOP |
| 36 | + -- Get max value from materialization hypertable converted to internal representation |
| 37 | + IF cagg_rec.column_type IN ('timestamptz'::regtype, 'timestamp'::regtype, 'date'::regtype) THEN |
| 38 | + EXECUTE format( |
| 39 | + 'SELECT (pg_catalog.date_part(''epoch'', pg_catalog.max(%I)) * 1000000)::bigint FROM %I.%I', |
| 40 | + cagg_rec.column_name, cagg_rec.schema_name, cagg_rec.table_name |
| 41 | + ) INTO max_val; |
| 42 | + ELSE |
| 43 | + EXECUTE format( |
| 44 | + 'SELECT pg_catalog.max(%I)::bigint FROM %I.%I', |
| 45 | + cagg_rec.column_name, cagg_rec.schema_name, cagg_rec.table_name |
| 46 | + ) INTO max_val; |
| 47 | + END IF; |
| 48 | + |
| 49 | + IF max_val IS NULL OR cagg_rec.bucket_width IS NULL OR NOT cagg_rec.bucket_fixed_width THEN |
| 50 | + -- No data, no bucket function info, or variable-width bucket: use minimum value. |
| 51 | + -- The next cagg refresh will compute the correct watermark. |
| 52 | + watermark_val := '-9223372036854775808'::bigint; |
| 53 | + ELSE |
| 54 | + -- Fixed-width bucket: watermark is max value + bucket width |
| 55 | + IF cagg_rec.column_type IN ('timestamptz'::regtype, 'timestamp'::regtype, 'date'::regtype) THEN |
| 56 | + bucket_width_val := (pg_catalog.date_part('epoch', cagg_rec.bucket_width::interval) * 1000000)::bigint; |
| 57 | + ELSE |
| 58 | + bucket_width_val := cagg_rec.bucket_width::bigint; |
| 59 | + END IF; |
| 60 | + watermark_val := max_val + bucket_width_val; |
| 61 | + END IF; |
| 62 | + |
| 63 | + INSERT INTO _timescaledb_catalog.continuous_aggs_watermark (mat_hypertable_id, watermark) |
| 64 | + VALUES (cagg_rec.mat_hypertable_id, watermark_val); |
| 65 | + END LOOP; |
| 66 | +END; |
| 67 | +$$; |
| 68 | + |
| 69 | +-- Cleanup orphaned compression settings |
| 70 | +WITH orphaned_settings AS ( |
| 71 | + SELECT cs.relid, cl.relname |
| 72 | + FROM _timescaledb_catalog.compression_settings cs |
| 73 | + LEFT JOIN pg_class cl ON (cs.relid = cl.oid) |
| 74 | + WHERE cl.relname IS NULL |
| 75 | +) |
| 76 | +DELETE FROM _timescaledb_catalog.compression_settings AS cs |
| 77 | +USING orphaned_settings AS os WHERE cs.relid = os.relid; |
| 78 | + |
| 79 | +-- Remove self-referential foreign keys to eliminate pg_dump circular dependency warnings |
| 80 | +ALTER TABLE _timescaledb_catalog.hypertable DROP CONSTRAINT IF EXISTS hypertable_compressed_hypertable_id_fkey; |
| 81 | +ALTER TABLE _timescaledb_catalog.chunk DROP CONSTRAINT IF EXISTS chunk_compressed_chunk_id_fkey; |
| 82 | + |
| 83 | + |
| 84 | +-- Block upgrade if bloom filter sparse indexes exist on smallint (int2) |
| 85 | +-- columns. These bloom filters used PostgreSQL's hashint2extended while |
| 86 | +-- the new code uses bloom1_hash_2. Existing bloom data must be dropped |
| 87 | +-- before upgrading; recompress afterwards to rebuild with the new hash. |
| 88 | +DO $$ |
| 89 | +DECLARE |
| 90 | + drop_commands text; |
| 91 | +BEGIN |
| 92 | + WITH bloom_entries AS ( |
| 93 | + SELECT relid AS chunk_oid, |
| 94 | + compress_relid, |
| 95 | + columns, |
| 96 | + (SELECT string_agg(col, '_' ORDER BY ordinality) |
| 97 | + FROM jsonb_array_elements_text(columns) |
| 98 | + WITH ORDINALITY AS t(col, ordinality)) AS col_suffix |
| 99 | + FROM _timescaledb_catalog.compression_settings, |
| 100 | + jsonb_array_elements(index) AS elem, |
| 101 | + LATERAL (SELECT |
| 102 | + CASE jsonb_typeof(elem->'column') |
| 103 | + WHEN 'array' THEN elem->'column' |
| 104 | + ELSE jsonb_build_array(elem->'column') |
| 105 | + END AS columns |
| 106 | + ) AS normalized |
| 107 | + WHERE elem->>'type' = 'bloom' |
| 108 | + AND compress_relid IS NOT NULL |
| 109 | + ), |
| 110 | + bloom_column_names AS ( |
| 111 | + SELECT chunk_oid, compress_relid, col_suffix, colname |
| 112 | + FROM bloom_entries, |
| 113 | + jsonb_array_elements_text(columns) AS bloom_column(colname) |
| 114 | + ), |
| 115 | + int2_bloom_suffixes AS ( |
| 116 | + SELECT DISTINCT compress_relid, col_suffix |
| 117 | + FROM bloom_column_names |
| 118 | + JOIN pg_attribute ON attrelid = chunk_oid |
| 119 | + AND attname = colname |
| 120 | + AND atttypid = 'int2'::regtype |
| 121 | + AND attnum > 0 |
| 122 | + ), |
| 123 | + bloom_cols_to_drop AS ( |
| 124 | + SELECT compress_relid, |
| 125 | + attname AS bloom_attname |
| 126 | + FROM int2_bloom_suffixes |
| 127 | + JOIN pg_attribute ON attrelid = compress_relid |
| 128 | + AND attname IN ( |
| 129 | + '_ts_meta_v2_bloom1_' || col_suffix, |
| 130 | + '_ts_meta_v2_bloomh_' || col_suffix, |
| 131 | + '_ts_meta_v2_bloomg_' || col_suffix |
| 132 | + ) |
| 133 | + AND attnum > 0 |
| 134 | + ) |
| 135 | + SELECT string_agg(DISTINCT |
| 136 | + format('ALTER TABLE %s DROP COLUMN %I;', |
| 137 | + compress_relid::regclass, bloom_attname), |
| 138 | + E'\n' ORDER BY |
| 139 | + format('ALTER TABLE %s DROP COLUMN %I;', |
| 140 | + compress_relid::regclass, bloom_attname)) |
| 141 | + INTO drop_commands |
| 142 | + FROM bloom_cols_to_drop; |
| 143 | + |
| 144 | + IF drop_commands IS NOT NULL THEN |
| 145 | + RAISE EXCEPTION |
| 146 | + 'existing bloom filter sparse indexes on smallint columns are incompatible ' |
| 147 | + 'with this version of TimescaleDB' |
| 148 | + USING |
| 149 | + DETAIL = E'These indexes must be dropped before upgrading. To do so, run the following commands:\n\n' |
| 150 | + || E'SET timescaledb.restoring = on;\n' |
| 151 | + || drop_commands || E'\n' |
| 152 | + || 'SET timescaledb.restoring = off;', |
| 153 | + HINT = 'To rebuild the bloom filter indexes after upgrading, decompress and compress the affected chunks.'; |
| 154 | + END IF; |
| 155 | +END |
| 156 | +$$; |
| 157 | + |
| 158 | + |
| 159 | +DROP FUNCTION IF EXISTS _timescaledb_functions.job_history_bsearch; |
| 160 | + |
| 161 | +DROP FUNCTION IF EXISTS _timescaledb_functions.policy_process_hypertable_invalidations_check(JSONB); |
| 162 | +DROP PROCEDURE IF EXISTS _timescaledb_functions.policy_process_hypertable_invalidations(INTEGER, JSONB); |
| 163 | +DROP PROCEDURE IF EXISTS @extschema@.add_process_hypertable_invalidations_policy(REGCLASS, INTERVAL, BOOL, TIMESTAMPTZ, TEXT); |
| 164 | +DROP PROCEDURE IF EXISTS @extschema@.remove_process_hypertable_invalidations_policy(REGCLASS, BOOL); |
| 165 | + |
| 166 | +-- Return type widened from INTEGER to BIGINT; per-batch byte count can |
| 167 | +-- exceed INT32_MAX for wide varlena columns and was silently wrapping. |
| 168 | +DROP FUNCTION IF EXISTS _timescaledb_functions.compressed_data_column_size(_timescaledb_internal.compressed_data, ANYELEMENT); |
| 169 | + |
| 170 | +-- Migration: refresh orderby sparse index entries in compression_settings |
| 171 | +UPDATE _timescaledb_catalog.compression_settings |
| 172 | +SET index = ( |
| 173 | + SELECT COALESCE(jsonb_agg(elem), '[]'::jsonb) |
| 174 | + FROM jsonb_array_elements(index) AS elem |
| 175 | + WHERE elem->>'source' != 'orderby' |
| 176 | +) |
| 177 | +WHERE index IS NOT NULL |
| 178 | +AND index @> '[{"source": "orderby"}]'; |
| 179 | + |
| 180 | +UPDATE _timescaledb_catalog.compression_settings cs |
| 181 | +SET index = COALESCE(index, '[]'::jsonb) || |
| 182 | + ( |
| 183 | + SELECT jsonb_agg(jsonb_build_object( |
| 184 | + 'type', 'minmax', |
| 185 | + 'source', 'orderby', |
| 186 | + 'column', elem)) |
| 187 | + FROM unnest(cs.orderby) AS elem |
| 188 | + ) |
| 189 | +WHERE cs.orderby IS NOT NULL; |
| 190 | + |
0 commit comments