Skip to content

Commit 0aea9ef

Browse files
committed
add timescaledb.compress_column_codec option
Instead of using types and default opclasses, the EXTERNAL compresison algorithm now uses regular opclasses and only applies when explicitly configured. Users alter or otherwise set the timescaledb.column_codec to a `column_name:some.compression_opclass`. The opclass, as in the previous commit, is a virtual namespace with 2 support functions. (1 and 2, compress and decompress respectively).
1 parent 937fbd0 commit 0aea9ef

47 files changed

Lines changed: 2173 additions & 762 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.unreleased/external-compression

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Implements: #1524 Add external compression, letting a type supply its own compress/decompress functions
1+
Implements: #1524 Add external compression: configure columns with timescaledb.compress_column_codec

cmake/ScriptFiles.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ set(PRE_INSTALL_SOURCE_FILES
1717
pre_install/types.post.sql # Must be before tables.sql
1818
pre_install/tables.sql
1919
pre_install/cache.sql
20+
pre_install/compression_codec.functions.sql
21+
pre_install/compression_codec.sql
2022
pre_install/insert_data.sql)
2123

2224
# Source files that define functions and need to be rerun in update
2325
set(PRE_INSTALL_FUNCTION_FILES
2426
pre_install/types.functions.sql
27+
pre_install/compression_codec.functions.sql
2528
)
2629

2730
# The rest of the source files defining mostly functions
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- This file and its contents are licensed under the Apache License 2.0.
2+
-- Please see the included NOTICE for copyright information and
3+
-- LICENSE-APACHE for a copy of the license.
4+
5+
CREATE OR REPLACE FUNCTION _timescaledb_functions.compression_codec_handler(internal) RETURNS index_am_handler
6+
AS '@MODULE_PATHNAME@', 'ts_compression_codec_handler' LANGUAGE C;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- This file and its contents are licensed under the Apache License 2.0.
2+
-- Please see the included NOTICE for copyright information and
3+
-- LICENSE-APACHE for a copy of the license.
4+
5+
-- Virtual access method: Register an EXTERNAL compression codec for a
6+
-- type by creating an operator class for this access method. Columns
7+
-- use a registered codec through the per-column setting
8+
-- timescaledb.compress_column_codec = '<column>:<operator class>, ...'
9+
-- * Support function 1: bytea compress(<type>[])
10+
-- * Support function 2: <type>[] decompress(bytea)
11+
CREATE ACCESS METHOD ts_compression_codec TYPE INDEX HANDLER _timescaledb_functions.compression_codec_handler;
12+
COMMENT ON ACCESS METHOD ts_compression_codec IS 'Registry for compression codec operator classes';

sql/pre_install/tables.sql

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,16 @@ CREATE TABLE _timescaledb_catalog.compression_settings (
460460
orderby_desc bool[],
461461
orderby_nullsfirst bool[],
462462
index jsonb,
463+
-- parallel arrays: codec_column[i] is compressed with the EXTERNAL codec
464+
-- registered by the ts_compression_codec operator class codec_opclass[i]
465+
codec_column text[],
466+
codec_opclass text[],
463467
CONSTRAINT compression_settings_pkey PRIMARY KEY (relid),
464468
CONSTRAINT compression_settings_check_segmentby CHECK (array_ndims(segmentby) = 1),
465469
CONSTRAINT compression_settings_check_orderby_null CHECK ((orderby IS NULL AND orderby_desc IS NULL AND orderby_nullsfirst IS NULL) OR (orderby IS NOT NULL AND orderby_desc IS NOT NULL AND orderby_nullsfirst IS NOT NULL)),
466-
CONSTRAINT compression_settings_check_orderby_cardinality CHECK (array_ndims(orderby) = 1 AND array_ndims(orderby_desc) = 1 AND array_ndims(orderby_nullsfirst) = 1 AND cardinality(orderby) = cardinality(orderby_desc) AND cardinality(orderby) = cardinality(orderby_nullsfirst))
470+
CONSTRAINT compression_settings_check_orderby_cardinality CHECK (array_ndims(orderby) = 1 AND array_ndims(orderby_desc) = 1 AND array_ndims(orderby_nullsfirst) = 1 AND cardinality(orderby) = cardinality(orderby_desc) AND cardinality(orderby) = cardinality(orderby_nullsfirst)),
471+
CONSTRAINT compression_settings_check_codec_null CHECK ((codec_column IS NULL AND codec_opclass IS NULL) OR (codec_column IS NOT NULL AND codec_opclass IS NOT NULL)),
472+
CONSTRAINT compression_settings_check_codec_cardinality CHECK (array_ndims(codec_column) = 1 AND array_ndims(codec_opclass) = 1 AND cardinality(codec_column) = cardinality(codec_opclass))
467473
);
468474

469475
SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.compression_settings', '');

sql/updates/latest-dev.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,21 @@ GRANT SELECT ON _timescaledb_catalog.chunk TO PUBLIC;
150150

151151
INSERT INTO _timescaledb_catalog.compression_algorithm( id, version, name, description) values
152152
( 8, 1, 'COMPRESSION_ALGORITHM_EXTERNAL', 'external');
153+
154+
-- Registry access method for EXTERNAL compression codec operator classes.
155+
-- Placeholder is replaced when the function definitions run later in the update.
156+
CREATE FUNCTION _timescaledb_functions.compression_codec_handler(internal) RETURNS index_am_handler
157+
AS '@MODULE_PATHNAME@', 'ts_update_placeholder' LANGUAGE C;
158+
CREATE ACCESS METHOD ts_compression_codec TYPE INDEX HANDLER _timescaledb_functions.compression_codec_handler;
159+
COMMENT ON ACCESS METHOD ts_compression_codec IS 'Registry for compression codec operator classes';
160+
161+
-- Per-column setting for EXTERNAL compression algorithm. Set via the
162+
-- timescaledb.compress_column_codec option. codec_column[i] is compressed
163+
-- with the codec operator class named by codec_opclass[i].
164+
ALTER TABLE _timescaledb_catalog.compression_settings
165+
ADD COLUMN codec_column text[],
166+
ADD COLUMN codec_opclass text[],
167+
ADD CONSTRAINT compression_settings_check_codec_null
168+
CHECK ((codec_column IS NULL AND codec_opclass IS NULL) OR (codec_column IS NOT NULL AND codec_opclass IS NOT NULL)),
169+
ADD CONSTRAINT compression_settings_check_codec_cardinality
170+
CHECK (array_ndims(codec_column) = 1 AND array_ndims(codec_opclass) = 1 AND cardinality(codec_column) = cardinality(codec_opclass));

sql/updates/reverse-dev.sql

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,81 @@ DROP FUNCTION IF EXISTS @extschema@.alter_job(job_id INTEGER, schedule_interval
252252

253253

254254
DELETE FROM _timescaledb_catalog.compression_algorithm WHERE id = 8 AND version = 1 AND name = 'COMPRESSION_ALGORITHM_EXTERNAL';
255+
256+
-- Can't downgrade while external compression codecs are registered. Chunks may
257+
-- still hold batches only their codec can read, and the older version has no
258+
-- EXTERNAL algorithm support.
259+
-- First, clear the timescaledb.compress_column_codec settings that reference
260+
-- the operator class so new compression uses the built-in algorithms. Then
261+
-- rewrite every chunk using the codec with decompress_chunk() followed
262+
-- by compress_chunk(). Then drop the operator class.
263+
DO $$
264+
DECLARE
265+
reg record;
266+
clear_cmds text;
267+
rewrite_cmds text;
268+
decommission_cmds text := NULL;
269+
codec_opclasses text := NULL;
270+
BEGIN
271+
FOR reg IN
272+
SELECT format('%I.%I', n.nspname, c.opcname) AS opclass,
273+
c.opcintype::regtype AS type
274+
FROM pg_catalog.pg_opclass c
275+
JOIN pg_catalog.pg_am a ON a.oid = c.opcmethod
276+
JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace
277+
WHERE a.amname = 'ts_compression_codec'
278+
LOOP
279+
codec_opclasses := concat_ws(', ', codec_opclasses, reg.opclass);
280+
281+
-- Clear the codec settings of every hypertable that references this
282+
-- operator class. The settings store the name the same way it is quoted
283+
-- here, so string comparison finds the references.
284+
SELECT string_agg(format('ALTER TABLE %s SET (timescaledb.compress_column_codec = '''');',
285+
format('%I.%I', h.schema_name, h.table_name)), E'\n')
286+
INTO clear_cmds
287+
FROM _timescaledb_catalog.hypertable h
288+
JOIN _timescaledb_catalog.compression_settings cs
289+
ON cs.relid = format('%I.%I', h.schema_name, h.table_name)::regclass
290+
WHERE reg.opclass = ANY (cs.codec_opclass);
291+
292+
-- For (comparative) simplicity, just show instructions for rewriting every
293+
-- possibly-affected chunk having a compressed column with this registered
294+
-- codec.
295+
SELECT string_agg(format(E'SELECT decompress_chunk(ch, true) FROM show_chunks(%L) ch;\n'
296+
'SELECT compress_chunk(ch) FROM show_chunks(%L) ch;', ht, ht), E'\n')
297+
INTO rewrite_cmds
298+
FROM (
299+
SELECT DISTINCT format('%I.%I', h.schema_name, h.table_name)::regclass AS ht
300+
FROM _timescaledb_catalog.hypertable h
301+
JOIN pg_catalog.pg_attribute att
302+
ON att.attrelid = format('%I.%I', h.schema_name, h.table_name)::regclass
303+
WHERE att.atttypid = reg.type AND att.attnum > 0 AND NOT att.attisdropped
304+
) affected;
305+
306+
decommission_cmds := concat_ws(E'\n', decommission_cmds, format(
307+
E'-- decommission codec %1$s for type %2$s\n'
308+
'%3$s'
309+
'%4$s'
310+
'DROP OPERATOR CLASS %1$s USING ts_compression_codec;',
311+
reg.opclass,
312+
reg.type,
313+
coalesce(clear_cmds || E'\n', ''),
314+
coalesce(rewrite_cmds || E'\n', '')));
315+
END LOOP;
316+
317+
IF codec_opclasses IS NOT NULL THEN
318+
RAISE EXCEPTION 'cannot downgrade while compression codec operator classes exist: %', codec_opclasses
319+
USING DETAIL = 'Chunks compressed while a codec is registered may hold EXTERNAL batches that the older version cannot read.',
320+
HINT = format(E'Run the following, then retry the downgrade:\n%s', decommission_cmds);
321+
END IF;
322+
END
323+
$$;
324+
DROP ACCESS METHOD IF EXISTS ts_compression_codec;
325+
DROP FUNCTION IF EXISTS _timescaledb_functions.compression_codec_handler(internal);
326+
327+
-- The older version has no per-column external codec setting.
328+
ALTER TABLE _timescaledb_catalog.compression_settings
329+
DROP CONSTRAINT compression_settings_check_codec_cardinality,
330+
DROP CONSTRAINT compression_settings_check_codec_null,
331+
DROP COLUMN codec_column,
332+
DROP COLUMN codec_opclass;

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ set(SOURCES
1111
chunk_insert_state.c
1212
chunk_scan.c
1313
chunk_tuple_routing.c
14+
compression_codec_am.c
1415
constraint.c
1516
cross_module_fn.c
1617
copy.c

0 commit comments

Comments
 (0)