Split segment meta min_max into two columns

This simplifies the code and the access to the min/max
metadata. Before we used a custom type, but now the min/max
are just the same type as the underlying column and stored as two
columns.

This also removes the custom type that was used before.
This commit is contained in:
Matvey Arye 2019-09-30 14:25:47 -04:00 committed by Matvey Arye
parent 4d12f5b8f3
commit 0f3e74215a
32 changed files with 914 additions and 1340 deletions

View File

@ -21,7 +21,6 @@ set(IMMUTABLE_API_SOURCE_FILES
set(SOURCE_FILES set(SOURCE_FILES
hypertable.sql hypertable.sql
chunk.sql chunk.sql
compression.sql
ddl_internal.sql ddl_internal.sql
edition.sql edition.sql
util_time.sql util_time.sql

View File

@ -1,18 +0,0 @@
-- This file and its contents are licensed under the Apache License 2.0.
-- Please see the included NOTICE for copyright information and
-- LICENSE-APACHE for a copy of the license.
CREATE FUNCTION _timescaledb_internal.segment_meta_get_min(_timescaledb_internal.segment_meta_min_max, ANYELEMENT)
RETURNS ANYELEMENT
AS '@MODULE_PATHNAME@', 'ts_segment_meta_get_min'
LANGUAGE C IMMUTABLE;
CREATE FUNCTION _timescaledb_internal.segment_meta_get_max(_timescaledb_internal.segment_meta_min_max, ANYELEMENT)
RETURNS ANYELEMENT
AS '@MODULE_PATHNAME@', 'ts_segment_meta_get_max'
LANGUAGE C IMMUTABLE;
CREATE FUNCTION _timescaledb_internal.segment_meta_has_null(_timescaledb_internal.segment_meta_min_max)
RETURNS boolean
AS '@MODULE_PATHNAME@', 'ts_segment_meta_has_null'
LANGUAGE C IMMUTABLE;

View File

@ -57,40 +57,3 @@ CREATE TYPE _timescaledb_internal.compressed_data (
RECEIVE = _timescaledb_internal.compressed_data_recv, RECEIVE = _timescaledb_internal.compressed_data_recv,
SEND = _timescaledb_internal.compressed_data_send SEND = _timescaledb_internal.compressed_data_send
); );
--
-- _timescaledb_internal.segment_meta_min_max keeps the min/max range of compressed data
--
CREATE TYPE _timescaledb_internal.segment_meta_min_max;
--the textual input/output is simply base64 encoding of the binary representation
CREATE FUNCTION _timescaledb_internal.segment_meta_min_max_in(CSTRING)
RETURNS _timescaledb_internal.segment_meta_min_max
AS '@MODULE_PATHNAME@', 'ts_segment_meta_min_max_in'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION _timescaledb_internal.segment_meta_min_max_out(_timescaledb_internal.segment_meta_min_max)
RETURNS CSTRING
AS '@MODULE_PATHNAME@', 'ts_segment_meta_min_max_out'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION _timescaledb_internal.segment_meta_min_max_send(_timescaledb_internal.segment_meta_min_max)
RETURNS BYTEA
AS '@MODULE_PATHNAME@', 'ts_segment_meta_min_max_send'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION _timescaledb_internal.segment_meta_min_max_recv(internal)
RETURNS _timescaledb_internal.segment_meta_min_max
AS '@MODULE_PATHNAME@', 'ts_segment_meta_min_max_recv'
LANGUAGE C IMMUTABLE STRICT;
CREATE TYPE _timescaledb_internal.segment_meta_min_max (
INTERNALLENGTH = VARIABLE,
STORAGE = EXTERNAL, --move to toast, don't compress
ALIGNMENT = DOUBLE, --needed for alignment to work with arbitrary datums
INPUT = _timescaledb_internal.segment_meta_min_max_in,
OUTPUT = _timescaledb_internal.segment_meta_min_max_out,
RECEIVE = _timescaledb_internal.segment_meta_min_max_recv,
SEND = _timescaledb_internal.segment_meta_min_max_send
);

View File

@ -214,40 +214,3 @@ insert into _timescaledb_catalog.compression_algorithm values
( 4, 1, 'COMPRESSION_ALGORITHM_DELTADELTA', 'deltadelta') ( 4, 1, 'COMPRESSION_ALGORITHM_DELTADELTA', 'deltadelta')
on conflict(id) do update set (version, name, description) on conflict(id) do update set (version, name, description)
= (excluded.version, excluded.name, excluded.description); = (excluded.version, excluded.name, excluded.description);
--
-- _timescaledb_internal.segment_meta_min_max keeps the min/max range of compressed data
--
CREATE TYPE _timescaledb_internal.segment_meta_min_max;
--the textual input/output is simply base64 encoding of the binary representation
CREATE FUNCTION _timescaledb_internal.segment_meta_min_max_in(CSTRING)
RETURNS _timescaledb_internal.segment_meta_min_max
AS '@MODULE_PATHNAME@', 'ts_segment_meta_min_max_in'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION _timescaledb_internal.segment_meta_min_max_out(_timescaledb_internal.segment_meta_min_max)
RETURNS CSTRING
AS '@MODULE_PATHNAME@', 'ts_segment_meta_min_max_out'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION _timescaledb_internal.segment_meta_min_max_send(_timescaledb_internal.segment_meta_min_max)
RETURNS BYTEA
AS '@MODULE_PATHNAME@', 'ts_segment_meta_min_max_send'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION _timescaledb_internal.segment_meta_min_max_recv(internal)
RETURNS _timescaledb_internal.segment_meta_min_max
AS '@MODULE_PATHNAME@', 'ts_segment_meta_min_max_recv'
LANGUAGE C IMMUTABLE STRICT;
CREATE TYPE _timescaledb_internal.segment_meta_min_max (
INTERNALLENGTH = VARIABLE,
STORAGE = EXTERNAL, --move to toast, don't compress
ALIGNMENT = DOUBLE, --needed for alignment to work with arbitrary datums
INPUT = _timescaledb_internal.segment_meta_min_max_in,
OUTPUT = _timescaledb_internal.segment_meta_min_max_out,
RECEIVE = _timescaledb_internal.segment_meta_min_max_recv,
SEND = _timescaledb_internal.segment_meta_min_max_send
);

View File

@ -19,7 +19,6 @@ set(SOURCES
copy.c copy.c
compression_chunk_size.c compression_chunk_size.c
compression_with_clause.c compression_with_clause.c
compression_segment_meta_min_max.c
dimension.c dimension.c
dimension_slice.c dimension_slice.c
dimension_vector.c dimension_vector.c

View File

@ -1,107 +0,0 @@
/*
* This file and its contents are licensed under the Apache License 2.0.
* Please see the included NOTICE for copyright information and
* LICENSE-APACHE for a copy of the license.
*/
#include <postgres.h>
#include "cross_module_fn.h"
#include "compat.h"
#include "base64_compat.h"
#include "license_guc.h"
TS_FUNCTION_INFO_V1(ts_segment_meta_min_max_send);
TS_FUNCTION_INFO_V1(ts_segment_meta_min_max_recv);
TS_FUNCTION_INFO_V1(ts_segment_meta_min_max_out);
TS_FUNCTION_INFO_V1(ts_segment_meta_min_max_in);
TS_FUNCTION_INFO_V1(ts_segment_meta_get_min);
TS_FUNCTION_INFO_V1(ts_segment_meta_get_max);
TS_FUNCTION_INFO_V1(ts_segment_meta_has_null);
Datum
ts_segment_meta_min_max_send(PG_FUNCTION_ARGS)
{
Datum meta = PG_GETARG_DATUM(0);
PG_RETURN_DATUM(PointerGetDatum(ts_cm_functions->segment_meta_min_max_send(meta)));
}
Datum
ts_segment_meta_min_max_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
PG_RETURN_DATUM(ts_cm_functions->segment_meta_min_max_recv(buf));
}
Datum
ts_segment_meta_min_max_out(PG_FUNCTION_ARGS)
{
Datum meta = PG_GETARG_DATUM(0);
bytea *bytes = ts_cm_functions->segment_meta_min_max_send(meta);
int raw_len = VARSIZE_ANY_EXHDR(bytes);
const char *raw_data = VARDATA(bytes);
int encoded_len = pg_b64_enc_len(raw_len);
char *encoded = palloc(encoded_len + 1);
encoded_len = pg_b64_encode(raw_data, raw_len, encoded);
encoded[encoded_len] = '\0';
PG_RETURN_CSTRING(encoded);
}
Datum
ts_segment_meta_min_max_in(PG_FUNCTION_ARGS)
{
const char *input = PG_GETARG_CSTRING(0);
size_t input_len = strlen(input);
int decoded_len;
char *decoded;
StringInfoData data;
/* Load TSL explicitly in case this is called during parsing */
ts_license_enable_module_loading();
if (input_len > PG_INT32_MAX)
elog(ERROR, "input too long");
decoded_len = pg_b64_dec_len(input_len);
decoded = palloc(decoded_len + 1);
decoded_len = pg_b64_decode(input, input_len, decoded);
decoded[decoded_len] = '\0';
data = (StringInfoData){
.data = decoded,
.len = decoded_len,
.maxlen = decoded_len,
};
PG_RETURN_DATUM(ts_cm_functions->segment_meta_min_max_recv(&data));
}
Datum
ts_segment_meta_get_min(PG_FUNCTION_ARGS)
{
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
PG_RETURN_DATUM(PointerGetDatum(
ts_cm_functions->segment_meta_get_min(PG_GETARG_DATUM(0),
get_fn_expr_argtype(fcinfo->flinfo, 1))));
}
Datum
ts_segment_meta_get_max(PG_FUNCTION_ARGS)
{
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
PG_RETURN_DATUM(PointerGetDatum(
ts_cm_functions->segment_meta_get_max(PG_GETARG_DATUM(0),
get_fn_expr_argtype(fcinfo->flinfo, 1))));
}
Datum
ts_segment_meta_has_null(PG_FUNCTION_ARGS)
{
if (PG_ARGISNULL(0))
PG_RETURN_BOOL(true);
PG_RETURN_BOOL(ts_cm_functions->segment_meta_has_null(PG_GETARG_DATUM(0)));
}

View File

@ -350,41 +350,6 @@ continuous_agg_drop_chunks_by_chunk_id_default(int32 raw_hypertable_id, Chunk **
error_no_default_fn_community(); error_no_default_fn_community();
} }
static bytea *
segment_meta_min_max_send_default(Datum arg1)
{
error_no_default_fn_community();
pg_unreachable();
}
static Datum
segment_meta_min_max_recv_default(StringInfo buf)
{
error_no_default_fn_community();
pg_unreachable();
}
static Datum
segment_meta_get_min_default(Datum meta, Oid type)
{
error_no_default_fn_community();
pg_unreachable();
}
static Datum
segment_meta_get_max_default(Datum meta, Oid type)
{
error_no_default_fn_community();
pg_unreachable();
}
static bool
segment_meta_has_null_default(Datum meta)
{
error_no_default_fn_community();
pg_unreachable();
}
/* /*
* Define cross-module functions' default values: * Define cross-module functions' default values:
* If the submodule isn't activated, using one of the cm functions will throw an * If the submodule isn't activated, using one of the cm functions will throw an
@ -437,12 +402,6 @@ TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = {
.process_compress_table = process_compress_table_default, .process_compress_table = process_compress_table_default,
.compress_chunk = error_no_default_fn_pg_community, .compress_chunk = error_no_default_fn_pg_community,
.decompress_chunk = error_no_default_fn_pg_community, .decompress_chunk = error_no_default_fn_pg_community,
.segment_meta_min_max_send = segment_meta_min_max_send_default,
.segment_meta_min_max_recv = segment_meta_min_max_recv_default,
.segment_meta_get_min = segment_meta_get_min_default,
.segment_meta_get_max = segment_meta_get_max_default,
.segment_meta_has_null = segment_meta_has_null_default,
.compressed_data_decompress_forward = error_no_default_fn_pg_community, .compressed_data_decompress_forward = error_no_default_fn_pg_community,
.compressed_data_decompress_reverse = error_no_default_fn_pg_community, .compressed_data_decompress_reverse = error_no_default_fn_pg_community,
.deltadelta_compressor_append = error_no_default_fn_pg_community, .deltadelta_compressor_append = error_no_default_fn_pg_community,

View File

@ -80,12 +80,6 @@ typedef struct CrossModuleFunctions
WithClauseResult *with_clause_options); WithClauseResult *with_clause_options);
PGFunction compress_chunk; PGFunction compress_chunk;
PGFunction decompress_chunk; PGFunction decompress_chunk;
bytea *(*segment_meta_min_max_send)(Datum);
Datum (*segment_meta_min_max_recv)(StringInfo buf);
Datum (*segment_meta_get_min)(Datum, Oid type);
Datum (*segment_meta_get_max)(Datum, Oid type);
bool (*segment_meta_has_null)(Datum);
/* The compression functions below are not installed in SQL as part of create extension; /* The compression functions below are not installed in SQL as part of create extension;
* They are installed and tested during testing scripts. They are exposed in cross-module * They are installed and tested during testing scripts. They are exposed in cross-module
* functions because they may be very useful for debugging customer problems if the sql * functions because they may be very useful for debugging customer problems if the sql

View File

@ -93,7 +93,8 @@ typedef struct PerColumn
* Information on the metadata we'll store for this column (currently only min/max). * Information on the metadata we'll store for this column (currently only min/max).
* Only used for order-by columns right now, will be {-1, NULL} for others. * Only used for order-by columns right now, will be {-1, NULL} for others.
*/ */
int16 min_max_metadata_attr_offset; int16 min_metadata_attr_offset;
int16 max_metadata_attr_offset;
SegmentMetaMinMaxBuilder *min_max_metadata_builder; SegmentMetaMinMaxBuilder *min_max_metadata_builder;
/* segment info; only used if compressor is NULL */ /* segment info; only used if compressor is NULL */
@ -479,7 +480,8 @@ row_compressor_init(RowCompressor *row_compressor, TupleDesc uncompressed_tuple_
Assert(AttrNumberGetAttrOffset(compressed_colnum) < num_columns_in_compressed_table); Assert(AttrNumberGetAttrOffset(compressed_colnum) < num_columns_in_compressed_table);
if (!COMPRESSIONCOL_IS_SEGMENT_BY(compression_info)) if (!COMPRESSIONCOL_IS_SEGMENT_BY(compression_info))
{ {
int16 segment_min_max_attr_offset = -1; int16 segment_min_attr_offset = -1;
int16 segment_max_attr_offset = -1;
SegmentMetaMinMaxBuilder *segment_min_max_builder = NULL; SegmentMetaMinMaxBuilder *segment_min_max_builder = NULL;
if (compressed_column_attr->atttypid != compressed_data_type_oid) if (compressed_column_attr->atttypid != compressed_data_type_oid)
elog(ERROR, elog(ERROR,
@ -488,12 +490,18 @@ row_compressor_init(RowCompressor *row_compressor, TupleDesc uncompressed_tuple_
if (compression_info->orderby_column_index > 0) if (compression_info->orderby_column_index > 0)
{ {
char *segment_col_name = compression_column_segment_min_max_name(compression_info); char *segment_min_col_name = compression_column_segment_min_name(compression_info);
AttrNumber segment_min_max_attr_number = char *segment_max_col_name = compression_column_segment_max_name(compression_info);
get_attnum(compressed_table->rd_id, segment_col_name); AttrNumber segment_min_attr_number =
if (segment_min_max_attr_number == InvalidAttrNumber) get_attnum(compressed_table->rd_id, segment_min_col_name);
elog(ERROR, "couldn't find metadata column %s", segment_col_name); AttrNumber segment_max_attr_number =
segment_min_max_attr_offset = AttrNumberGetAttrOffset(segment_min_max_attr_number); get_attnum(compressed_table->rd_id, segment_max_col_name);
if (segment_min_attr_number == InvalidAttrNumber)
elog(ERROR, "couldn't find metadata column %s", segment_min_col_name);
if (segment_max_attr_number == InvalidAttrNumber)
elog(ERROR, "couldn't find metadata column %s", segment_max_col_name);
segment_min_attr_offset = AttrNumberGetAttrOffset(segment_min_attr_number);
segment_max_attr_offset = AttrNumberGetAttrOffset(segment_max_attr_number);
segment_min_max_builder = segment_min_max_builder =
segment_meta_min_max_builder_create(column_attr->atttypid, segment_meta_min_max_builder_create(column_attr->atttypid,
column_attr->attcollation); column_attr->attcollation);
@ -501,7 +509,8 @@ row_compressor_init(RowCompressor *row_compressor, TupleDesc uncompressed_tuple_
*column = (PerColumn){ *column = (PerColumn){
.compressor = compressor_for_algorithm_and_type(compression_info->algo_id, .compressor = compressor_for_algorithm_and_type(compression_info->algo_id,
column_attr->atttypid), column_attr->atttypid),
.min_max_metadata_attr_offset = segment_min_max_attr_offset, .min_metadata_attr_offset = segment_min_attr_offset,
.max_metadata_attr_offset = segment_max_attr_offset,
.min_max_metadata_builder = segment_min_max_builder, .min_max_metadata_builder = segment_min_max_builder,
}; };
} }
@ -513,7 +522,8 @@ row_compressor_init(RowCompressor *row_compressor, TupleDesc uncompressed_tuple_
compression_info->attname.data); compression_info->attname.data);
*column = (PerColumn){ *column = (PerColumn){
.segment_info = segment_info_new(column_attr), .segment_info = segment_info_new(column_attr),
.min_max_metadata_attr_offset = -1, .min_metadata_attr_offset = -1,
.max_metadata_attr_offset = -1,
}; };
} }
} }
@ -700,21 +710,26 @@ row_compressor_flush(RowCompressor *row_compressor, CommandId mycid, bool change
if (column->min_max_metadata_builder != NULL) if (column->min_max_metadata_builder != NULL)
{ {
SegmentMetaMinMax *segment_meta_min_max = Assert(column->min_metadata_attr_offset >= 0);
segment_meta_min_max_builder_finish_and_reset(column->min_max_metadata_builder); Assert(column->max_metadata_attr_offset >= 0);
Assert(column->min_max_metadata_attr_offset >= 0); if (!segment_meta_min_max_builder_empty(column->min_max_metadata_builder))
{
Assert(compressed_data != NULL);
row_compressor->compressed_is_null[column->min_metadata_attr_offset] = false;
row_compressor->compressed_is_null[column->max_metadata_attr_offset] = false;
/* both the data and metadata are only NULL if all the data is NULL, thus: either row_compressor->compressed_values[column->min_metadata_attr_offset] =
* both the data and the metadata are both null or neither are */ segment_meta_min_max_builder_min(column->min_max_metadata_builder);
Assert((compressed_data == NULL && segment_meta_min_max == NULL) || row_compressor->compressed_values[column->max_metadata_attr_offset] =
(compressed_data != NULL && segment_meta_min_max != NULL)); segment_meta_min_max_builder_max(column->min_max_metadata_builder);
}
row_compressor->compressed_is_null[column->min_max_metadata_attr_offset] = else
segment_meta_min_max == NULL; {
if (segment_meta_min_max != NULL) Assert(compressed_data == NULL);
row_compressor->compressed_values[column->min_max_metadata_attr_offset] = row_compressor->compressed_is_null[column->min_metadata_attr_offset] = true;
PointerGetDatum(segment_meta_min_max); row_compressor->compressed_is_null[column->max_metadata_attr_offset] = true;
}
} }
} }
else if (column->segment_info != NULL) else if (column->segment_info != NULL)
@ -768,13 +783,20 @@ row_compressor_flush(RowCompressor *row_compressor, CommandId mycid, bool change
if (column->compressor != NULL || !column->segment_info->typ_by_val) if (column->compressor != NULL || !column->segment_info->typ_by_val)
pfree(DatumGetPointer(row_compressor->compressed_values[compressed_col])); pfree(DatumGetPointer(row_compressor->compressed_values[compressed_col]));
if (column->min_max_metadata_builder != NULL && if (column->min_max_metadata_builder != NULL)
row_compressor->compressed_is_null[column->min_max_metadata_attr_offset])
{ {
pfree(DatumGetPointer( /* segment_meta_min_max_builder_reset will free the values, so clear here */
row_compressor->compressed_values[column->min_max_metadata_attr_offset])); if (!row_compressor->compressed_is_null[column->min_metadata_attr_offset])
row_compressor->compressed_values[column->min_max_metadata_attr_offset] = 0; {
row_compressor->compressed_is_null[column->min_max_metadata_attr_offset] = true; row_compressor->compressed_values[column->min_metadata_attr_offset] = 0;
row_compressor->compressed_is_null[column->min_metadata_attr_offset] = true;
}
if (!row_compressor->compressed_is_null[column->max_metadata_attr_offset])
{
row_compressor->compressed_values[column->max_metadata_attr_offset] = 0;
row_compressor->compressed_is_null[column->max_metadata_attr_offset] = true;
}
segment_meta_min_max_builder_reset(column->min_max_metadata_builder);
} }
row_compressor->compressed_values[compressed_col] = 0; row_compressor->compressed_values[compressed_col] = 0;

View File

@ -20,6 +20,7 @@
#include <utils/builtins.h> #include <utils/builtins.h>
#include <utils/rel.h> #include <utils/rel.h>
#include <utils/syscache.h> #include <utils/syscache.h>
#include <utils/typcache.h>
#include "catalog.h" #include "catalog.h"
#include "create.h" #include "create.h"
@ -91,12 +92,20 @@ get_default_algorithm_id(Oid typeoid)
return COMPRESSION_ALGORITHM_DICTIONARY; return COMPRESSION_ALGORITHM_DICTIONARY;
} }
default: default:
{
/* use dictitionary if possible, otherwise use array */
TypeCacheEntry *tentry =
lookup_type_cache(typeoid, TYPECACHE_EQ_OPR_FINFO | TYPECACHE_HASH_PROC_FINFO);
if (tentry->hash_proc_finfo.fn_addr == NULL || tentry->eq_opr_finfo.fn_addr == NULL)
return COMPRESSION_ALGORITHM_ARRAY;
return COMPRESSION_ALGORITHM_DICTIONARY; return COMPRESSION_ALGORITHM_DICTIONARY;
}
} }
} }
char * static char *
compression_column_segment_min_max_name(const FormData_hypertable_compression *fd) compression_column_segment_metadata_name(const FormData_hypertable_compression *fd,
const char *type)
{ {
char *buf = palloc(sizeof(char) * NAMEDATALEN); char *buf = palloc(sizeof(char) * NAMEDATALEN);
int ret; int ret;
@ -104,26 +113,36 @@ compression_column_segment_min_max_name(const FormData_hypertable_compression *f
Assert(fd->orderby_column_index > 0); Assert(fd->orderby_column_index > 0);
ret = snprintf(buf, ret = snprintf(buf,
NAMEDATALEN, NAMEDATALEN,
COMPRESSION_COLUMN_METADATA_PREFIX "min_max_%d", COMPRESSION_COLUMN_METADATA_PREFIX "%s_%d",
type,
fd->orderby_column_index); fd->orderby_column_index);
if (ret < 0 || ret > NAMEDATALEN) if (ret < 0 || ret > NAMEDATALEN)
{ {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR), (errcode(ERRCODE_INTERNAL_ERROR), errmsg("bad segment metadata column name")));
errmsg("bad segment metadata min max column name")));
} }
return buf; return buf;
} }
char *
compression_column_segment_min_name(const FormData_hypertable_compression *fd)
{
return compression_column_segment_metadata_name(fd, "min");
}
char *
compression_column_segment_max_name(const FormData_hypertable_compression *fd)
{
return compression_column_segment_metadata_name(fd, "max");
}
static void static void
compresscolinfo_add_metadata_columns(CompressColInfo *cc) compresscolinfo_add_metadata_columns(CompressColInfo *cc, Relation uncompressed_rel)
{ {
/* additional metadata columns. /* additional metadata columns.
* these are not listed in hypertable_compression catalog table * these are not listed in hypertable_compression catalog table
* and so only has a ColDef entry */ * and so only has a ColDef entry */
int colno; int colno;
Oid segment_meta_min_max_oid =
ts_custom_type_cache_get(CUSTOM_TYPE_SEGMENT_META_MIN_MAX)->type_oid;
/* count column */ /* count column */
cc->coldeflist = lappend(cc->coldeflist, cc->coldeflist = lappend(cc->coldeflist,
@ -146,11 +165,30 @@ compresscolinfo_add_metadata_columns(CompressColInfo *cc)
{ {
if (cc->col_meta[colno].orderby_column_index > 0) if (cc->col_meta[colno].orderby_column_index > 0)
{ {
/* segment_meta_min_max columns */ FormData_hypertable_compression fd = cc->col_meta[colno];
AttrNumber col_attno = get_attnum(uncompressed_rel->rd_id, NameStr(fd.attname));
Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(uncompressed_rel),
AttrNumberGetAttrOffset(col_attno));
TypeCacheEntry *type = lookup_type_cache(attr->atttypid, TYPECACHE_LT_OPR);
if (!OidIsValid(type->lt_opr))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("invalid order by column type: could not identify an less-than "
"operator for type %s",
format_type_be(attr->atttypid))));
/* segment_meta min and max columns */
cc->coldeflist = cc->coldeflist =
lappend(cc->coldeflist, lappend(cc->coldeflist,
makeColumnDef(compression_column_segment_min_max_name(&cc->col_meta[colno]), makeColumnDef(compression_column_segment_min_name(&cc->col_meta[colno]),
segment_meta_min_max_oid, attr->atttypid,
-1 /* typemod */,
0 /*collation*/));
cc->coldeflist =
lappend(cc->coldeflist,
makeColumnDef(compression_column_segment_max_name(&cc->col_meta[colno]),
attr->atttypid,
-1 /* typemod */, -1 /* typemod */,
0 /*collation*/)); 0 /*collation*/));
} }
@ -271,7 +309,7 @@ compresscolinfo_init(CompressColInfo *cc, Oid srctbl_relid, List *segmentby_cols
} }
cc->numcols = colno; cc->numcols = colno;
compresscolinfo_add_metadata_columns(cc); compresscolinfo_add_metadata_columns(cc, rel);
relation_close(rel, AccessShareLock); relation_close(rel, AccessShareLock);
} }

View File

@ -21,6 +21,7 @@ bool tsl_process_compress_table(AlterTableCmd *cmd, Hypertable *ht,
WithClauseResult *with_clause_options); WithClauseResult *with_clause_options);
Chunk *create_compress_chunk_table(Hypertable *compress_ht, Chunk *src_chunk); Chunk *create_compress_chunk_table(Hypertable *compress_ht, Chunk *src_chunk);
char *compression_column_segment_min_max_name(const FormData_hypertable_compression *fd); char *compression_column_segment_min_name(const FormData_hypertable_compression *fd);
char *compression_column_segment_max_name(const FormData_hypertable_compression *fd);
#endif /* TIMESCALEDB_TSL_COMPRESSION_CREATE_H */ #endif /* TIMESCALEDB_TSL_COMPRESSION_CREATE_H */

View File

@ -27,40 +27,6 @@ typedef struct SegmentMetaMinMaxBuilder
Datum max; Datum max;
} SegmentMetaMinMaxBuilder; } SegmentMetaMinMaxBuilder;
typedef enum SegmentMetaMinMaxVersion
{
/* Not a real version, if this does get used, it's a bug in the code */
_INVALID_SEGMENT_MIN_MAX_VERSION = 0,
SEGMENT_SEGMENT_MIN_MAX_V1,
/* end of real values */
_MAX_SEGMENT_MIN_MAX_VERSION = 128,
} SegmentMetaMinMaxVersion;
typedef enum SegmentMetaMinMaxFlags
{
/* Has nulls allows us to optimize IS NULL quals */
HAS_NULLS = (1 << 0),
/* All Nulls should result in a NULL value for entire SegmentMetaMinMax */
_MAX_FLAG = (1 << 8),
} SegmentMetaMinMaxFlags;
/* start must be aligned according to the alignment of the stored type */
typedef struct SegmentMetaMinMax
{
char vl_len_[4];
uint8 version; /* SegmentMetaMinMaxVersion */
uint8 flags; /* SegmentMetaMinMaxFlags */
char padding[2];
Oid type;
/* optional alignment padding for type */
/*char data[FLEXIBLE_ARRAY_MEMBER]; bound values for two datums with alignment padding in
* between. First datum is min, second is max. Size determined by the datum type or the VARLENA
* header */
} SegmentMetaMinMax;
SegmentMetaMinMaxBuilder * SegmentMetaMinMaxBuilder *
segment_meta_min_max_builder_create(Oid type_oid, Oid collation) segment_meta_min_max_builder_create(Oid type_oid, Oid collation)
{ {
@ -105,11 +71,19 @@ segment_meta_min_max_builder_update_val(SegmentMetaMinMaxBuilder *builder, Datum
cmp = ApplySortComparator(builder->min, false, val, false, &builder->ssup); cmp = ApplySortComparator(builder->min, false, val, false, &builder->ssup);
if (cmp > 0) if (cmp > 0)
{
if (!builder->type_by_val)
pfree(DatumGetPointer(builder->min));
builder->min = datumCopy(val, builder->type_by_val, builder->type_len); builder->min = datumCopy(val, builder->type_by_val, builder->type_len);
}
cmp = ApplySortComparator(builder->max, false, val, false, &builder->ssup); cmp = ApplySortComparator(builder->max, false, val, false, &builder->ssup);
if (cmp < 0) if (cmp < 0)
{
if (!builder->type_by_val)
pfree(DatumGetPointer(builder->max));
builder->max = datumCopy(val, builder->type_by_val, builder->type_len); builder->max = datumCopy(val, builder->type_by_val, builder->type_len);
}
} }
void void
@ -118,7 +92,7 @@ segment_meta_min_max_builder_update_null(SegmentMetaMinMaxBuilder *builder)
builder->has_null = true; builder->has_null = true;
} }
static void void
segment_meta_min_max_builder_reset(SegmentMetaMinMaxBuilder *builder) segment_meta_min_max_builder_reset(SegmentMetaMinMaxBuilder *builder)
{ {
if (!builder->empty) if (!builder->empty)
@ -135,149 +109,38 @@ segment_meta_min_max_builder_reset(SegmentMetaMinMaxBuilder *builder)
builder->has_null = false; builder->has_null = false;
} }
SegmentMetaMinMax * Datum
segment_meta_min_max_builder_finish(SegmentMetaMinMaxBuilder *builder) segment_meta_min_max_builder_min(SegmentMetaMinMaxBuilder *builder)
{ {
SegmentMetaMinMax *res;
uint8 flags = 0;
Size total_size = sizeof(*res);
DatumSerializer *serializer;
char *data;
if (builder->empty) if (builder->empty)
return NULL; elog(ERROR, "trying to get min from an empty builder");
serializer = create_datum_serializer(builder->type_oid);
if (builder->has_null)
flags |= HAS_NULLS;
if (builder->type_len == -1) if (builder->type_len == -1)
{ {
/* detoast if necessary. should never store toast pointers */ Datum unpacked = PointerGetDatum(PG_DETOAST_DATUM_PACKED(builder->min));
builder->min = PointerGetDatum(PG_DETOAST_DATUM_PACKED(builder->min)); if (builder->min != unpacked)
builder->max = PointerGetDatum(PG_DETOAST_DATUM_PACKED(builder->max)); pfree(DatumGetPointer(builder->min));
builder->min = unpacked;
} }
return builder->min;
total_size = datum_get_bytes_size(serializer, total_size, builder->min);
total_size = datum_get_bytes_size(serializer, total_size, builder->max);
res = palloc0(total_size);
*res = (SegmentMetaMinMax){
.version = SEGMENT_SEGMENT_MIN_MAX_V1,
.flags = flags,
.type = builder->type_oid,
};
SET_VARSIZE(res, total_size);
data = (char *) res + sizeof(*res);
total_size -= sizeof(*res);
data = datum_to_bytes_and_advance(serializer, data, &total_size, builder->min);
data = datum_to_bytes_and_advance(serializer, data, &total_size, builder->max);
Assert(total_size == 0);
return res;
} }
SegmentMetaMinMax * Datum
segment_meta_min_max_builder_finish_and_reset(SegmentMetaMinMaxBuilder *builder) segment_meta_min_max_builder_max(SegmentMetaMinMaxBuilder *builder)
{ {
SegmentMetaMinMax *res = segment_meta_min_max_builder_finish(builder); if (builder->empty)
segment_meta_min_max_builder_reset(builder); elog(ERROR, "trying to get max from an empty builder");
return res; if (builder->type_len == -1)
}
static void
segment_meta_min_max_get_deconstruct(SegmentMetaMinMax *meta, DatumDeserializer *deser, Datum *min,
Datum *max)
{
const char *data = (char *) meta + sizeof(*meta);
/* skip the min */
*min = bytes_to_datum_and_advance(deser, &data);
*max = bytes_to_datum_and_advance(deser, &data);
}
bytea *
segment_meta_min_max_to_binary_string(SegmentMetaMinMax *meta)
{
StringInfoData buf;
DatumDeserializer *deser = create_datum_deserializer(meta->type);
DatumSerializer *ser = create_datum_serializer(meta->type);
Datum min, max;
segment_meta_min_max_get_deconstruct(meta, deser, &min, &max);
pq_begintypsend(&buf);
pq_sendbyte(&buf, meta->version);
pq_sendbyte(&buf, meta->flags);
type_append_to_binary_string(meta->type, &buf);
datum_append_to_binary_string(ser, MESSAGE_SPECIFIES_ENCODING, &buf, min);
datum_append_to_binary_string(ser, MESSAGE_SPECIFIES_ENCODING, &buf, max);
return pq_endtypsend(&buf);
}
SegmentMetaMinMax *
segment_meta_min_max_from_binary_string(StringInfo buf)
{
uint8 version = pq_getmsgbyte(buf);
if (version == SEGMENT_SEGMENT_MIN_MAX_V1)
{ {
uint8 flags = pq_getmsgbyte(buf); Datum unpacked = PointerGetDatum(PG_DETOAST_DATUM_PACKED(builder->max));
Oid type_oid = binary_string_get_type(buf); if (builder->max != unpacked)
DatumDeserializer *deser = create_datum_deserializer(type_oid); pfree(DatumGetPointer(builder->max));
TypeCacheEntry *type = lookup_type_cache(type_oid, 0); builder->max = unpacked;
SegmentMetaMinMaxBuilder builder = (SegmentMetaMinMaxBuilder){
.type_oid = type_oid,
.empty = false,
.has_null = (flags & HAS_NULLS) != 0,
.type_by_val = type->typbyval,
.type_len = type->typlen,
.min = binary_string_to_datum(deser, MESSAGE_SPECIFIES_ENCODING, buf),
.max = binary_string_to_datum(deser, MESSAGE_SPECIFIES_ENCODING, buf),
};
return segment_meta_min_max_builder_finish(&builder);
} }
else return builder->max;
elog(ERROR, "Unknown version number for segment meta min max: %d", version);
}
Datum
tsl_segment_meta_get_min(Datum meta_datum, Oid type)
{
SegmentMetaMinMax *meta = (SegmentMetaMinMax *) PG_DETOAST_DATUM(meta_datum);
DatumDeserializer *deser;
Datum min, max;
if (type != meta->type)
elog(ERROR, "wrong type requested from segment_meta_min_max");
deser = create_datum_deserializer(meta->type);
segment_meta_min_max_get_deconstruct(meta, deser, &min, &max);
return min;
}
Datum
tsl_segment_meta_get_max(Datum meta_datum, Oid type)
{
SegmentMetaMinMax *meta = (SegmentMetaMinMax *) PG_DETOAST_DATUM(meta_datum);
DatumDeserializer *deser = create_datum_deserializer(meta->type);
Datum min, max;
if (type != meta->type)
elog(ERROR, "wrong type requested from segment_meta_min_max");
segment_meta_min_max_get_deconstruct(meta, deser, &min, &max);
return max;
} }
bool bool
tsl_segment_meta_has_null(Datum meta_datum) segment_meta_min_max_builder_empty(SegmentMetaMinMaxBuilder *builder)
{ {
SegmentMetaMinMax *meta = (SegmentMetaMinMax *) PG_DETOAST_DATUM(meta_datum); return builder->empty;
return (meta->flags & HAS_NULLS) != 0;
} }

View File

@ -10,36 +10,15 @@
#include <fmgr.h> #include <fmgr.h>
#include <lib/stringinfo.h> #include <lib/stringinfo.h>
typedef struct SegmentMetaMinMax SegmentMetaMinMax;
typedef struct SegmentMetaMinMaxBuilder SegmentMetaMinMaxBuilder; typedef struct SegmentMetaMinMaxBuilder SegmentMetaMinMaxBuilder;
#define SEGMENT_META_ACCESSOR_MAX_SQL_FUNCTION "segment_meta_get_max"
#define SEGMENT_META_ACCESSOR_MIN_SQL_FUNCTION "segment_meta_get_min"
SegmentMetaMinMaxBuilder *segment_meta_min_max_builder_create(Oid type, Oid collation); SegmentMetaMinMaxBuilder *segment_meta_min_max_builder_create(Oid type, Oid collation);
void segment_meta_min_max_builder_update_val(SegmentMetaMinMaxBuilder *builder, Datum val); void segment_meta_min_max_builder_update_val(SegmentMetaMinMaxBuilder *builder, Datum val);
void segment_meta_min_max_builder_update_null(SegmentMetaMinMaxBuilder *builder); void segment_meta_min_max_builder_update_null(SegmentMetaMinMaxBuilder *builder);
SegmentMetaMinMax *segment_meta_min_max_builder_finish(SegmentMetaMinMaxBuilder *builder);
SegmentMetaMinMax *segment_meta_min_max_builder_finish_and_reset(SegmentMetaMinMaxBuilder *builder);
Datum tsl_segment_meta_get_min(Datum meta, Oid type); Datum segment_meta_min_max_builder_min(SegmentMetaMinMaxBuilder *builder);
Datum tsl_segment_meta_get_max(Datum meta, Oid type); Datum segment_meta_min_max_builder_max(SegmentMetaMinMaxBuilder *builder);
bool tsl_segment_meta_has_null(Datum meta); bool segment_meta_min_max_builder_empty(SegmentMetaMinMaxBuilder *builder);
bytea *segment_meta_min_max_to_binary_string(SegmentMetaMinMax *meta); void segment_meta_min_max_builder_reset(SegmentMetaMinMaxBuilder *builder);
SegmentMetaMinMax *segment_meta_min_max_from_binary_string(StringInfo buf);
static inline bytea *
tsl_segment_meta_min_max_send(Datum arg1)
{
SegmentMetaMinMax *meta = (SegmentMetaMinMax *) PG_DETOAST_DATUM(arg1);
return segment_meta_min_max_to_binary_string(meta);
}
static inline Datum
tsl_segment_meta_min_max_recv(StringInfo buf)
{
return PointerGetDatum(segment_meta_min_max_from_binary_string(buf));
}
#endif #endif

View File

@ -268,13 +268,17 @@ compressed_rel_setup_reltarget(RelOptInfo *compressed_rel, CompressionInfo *info
compressed_reltarget_add_var_for_column(compressed_rel, compressed_relid, column_name); compressed_reltarget_add_var_for_column(compressed_rel, compressed_relid, column_name);
/* if the column is an orderby, add it's metadata column too */ /* if the column is an orderby, add it's metadata columns too */
if (column_info->orderby_column_index > 0) if (column_info->orderby_column_index > 0)
{ {
column_name = compression_column_segment_min_max_name(column_info);
compressed_reltarget_add_var_for_column(compressed_rel, compressed_reltarget_add_var_for_column(compressed_rel,
compressed_relid, compressed_relid,
column_name); compression_column_segment_min_name(
column_info));
compressed_reltarget_add_var_for_column(compressed_rel,
compressed_relid,
compression_column_segment_max_name(
column_info));
} }
} }
} }

View File

@ -109,75 +109,43 @@ get_compression_info_from_var(QualPushdownContext *context, Var *var)
return get_column_compressioninfo(context->compression_info, column_name); return get_column_compressioninfo(context->compression_info, column_name);
} }
static FuncExpr *
make_segment_meta_accessor_funcexpr(int strategy, Index compressed_rel_index,
AttrNumber meta_column_attno, Oid uncompressed_type_oid,
Oid result_collation)
{
List *func_name;
Oid segment_meta_type = ts_custom_type_cache_get(CUSTOM_TYPE_SEGMENT_META_MIN_MAX)->type_oid;
Oid accessor_function_oid;
Oid argtypes[] = { segment_meta_type, ANYELEMENTOID };
Var *meta_var;
Const *null_const;
switch (strategy)
{
case BTGreaterStrategyNumber:
case BTGreaterEqualStrategyNumber:
/* var > expr implies max > expr */
func_name = list_make2(makeString(INTERNAL_SCHEMA_NAME),
makeString(SEGMENT_META_ACCESSOR_MAX_SQL_FUNCTION));
break;
case BTLessStrategyNumber:
case BTLessEqualStrategyNumber:
/* var < expr implies min < expr */
func_name = list_make2(makeString(INTERNAL_SCHEMA_NAME),
makeString(SEGMENT_META_ACCESSOR_MIN_SQL_FUNCTION));
break;
default:
elog(ERROR, "invalid strategy");
break;
}
accessor_function_oid = LookupFuncName(func_name, lengthof(argtypes), argtypes, false);
meta_var =
makeVar(compressed_rel_index, meta_column_attno, segment_meta_type, -1, InvalidOid, 0);
null_const = makeNullConst(uncompressed_type_oid, -1, InvalidOid);
return makeFuncExpr(accessor_function_oid,
uncompressed_type_oid,
list_make2(meta_var, null_const),
result_collation,
InvalidOid,
0);
}
static OpExpr * static OpExpr *
make_segment_meta_opexpr(QualPushdownContext *context, Oid opno, AttrNumber meta_column_attno, make_segment_meta_opexpr(QualPushdownContext *context, Oid opno, AttrNumber meta_column_attno,
Var *uncompressed_var, Expr *compare_to_expr, StrategyNumber strategy) Var *uncompressed_var, Expr *compare_to_expr, StrategyNumber strategy)
{ {
FuncExpr *func = make_segment_meta_accessor_funcexpr(strategy, Var *meta_var = makeVar(context->compressed_rel->relid,
context->compressed_rel->relid, meta_column_attno,
meta_column_attno, uncompressed_var->vartype,
uncompressed_var->vartype, -1,
uncompressed_var->varcollid); InvalidOid,
0);
return (OpExpr *) make_opclause(opno, return (OpExpr *) make_opclause(opno,
BOOLOID, BOOLOID,
false, false,
(Expr *) func, (Expr *) meta_var,
copyObject(compare_to_expr), copyObject(compare_to_expr),
InvalidOid, InvalidOid,
uncompressed_var->varcollid); uncompressed_var->varcollid);
} }
static AttrNumber static AttrNumber
get_segment_meta_attr_number(FormData_hypertable_compression *compression_info, get_segment_meta_min_attr_number(FormData_hypertable_compression *compression_info,
Oid compressed_relid) Oid compressed_relid)
{ {
char *meta_col_name = compression_column_segment_min_max_name(compression_info); char *meta_col_name = compression_column_segment_min_name(compression_info);
if (meta_col_name == NULL)
elog(ERROR, "could not find meta column");
return get_attnum(compressed_relid, meta_col_name);
}
static AttrNumber
get_segment_meta_max_attr_number(FormData_hypertable_compression *compression_info,
Oid compressed_relid)
{
char *meta_col_name = compression_column_segment_max_name(compression_info);
if (meta_col_name == NULL) if (meta_col_name == NULL)
elog(ERROR, "could not find meta column"); elog(ERROR, "could not find meta column");
@ -282,8 +250,6 @@ pushdown_op_to_segment_meta_min_max(QualPushdownContext *context, List *expr_arg
case BTEqualStrategyNumber: case BTEqualStrategyNumber:
{ {
/* var = expr implies min < expr and max > expr */ /* var = expr implies min < expr and max > expr */
AttrNumber meta_attno =
get_segment_meta_attr_number(compression_info, context->compressed_rte->relid);
Oid opno_le = get_opfamily_member(tce->btree_opf, Oid opno_le = get_opfamily_member(tce->btree_opf,
tce->type_id, tce->type_id,
tce->type_id, tce->type_id,
@ -296,39 +262,67 @@ pushdown_op_to_segment_meta_min_max(QualPushdownContext *context, List *expr_arg
if (!OidIsValid(opno_le) || !OidIsValid(opno_ge)) if (!OidIsValid(opno_le) || !OidIsValid(opno_ge))
return NULL; return NULL;
return make_andclause( return make_andclause(list_make2(
list_make2(make_segment_meta_opexpr(context, make_segment_meta_opexpr(context,
opno_le, opno_le,
meta_attno, get_segment_meta_min_attr_number(compression_info,
var_with_segment_meta, context->compressed_rte
expr, ->relid),
BTLessEqualStrategyNumber), var_with_segment_meta,
make_segment_meta_opexpr(context, expr,
opno_ge, BTLessEqualStrategyNumber),
meta_attno, make_segment_meta_opexpr(context,
var_with_segment_meta, opno_ge,
expr, get_segment_meta_max_attr_number(compression_info,
BTGreaterEqualStrategyNumber))); context->compressed_rte
->relid),
var_with_segment_meta,
expr,
BTGreaterEqualStrategyNumber)));
} }
case BTLessStrategyNumber: case BTLessStrategyNumber:
case BTLessEqualStrategyNumber: case BTLessEqualStrategyNumber:
/* var < expr implies min < expr */
{
Oid opno =
get_opfamily_member(tce->btree_opf, tce->type_id, tce->type_id, strategy);
if (!OidIsValid(opno))
return NULL;
return (Expr *)
make_segment_meta_opexpr(context,
opno,
get_segment_meta_min_attr_number(compression_info,
context
->compressed_rte
->relid),
var_with_segment_meta,
expr,
strategy);
}
case BTGreaterStrategyNumber: case BTGreaterStrategyNumber:
case BTGreaterEqualStrategyNumber: case BTGreaterEqualStrategyNumber:
{ /* var > expr implies max > expr */
AttrNumber meta_attno = {
get_segment_meta_attr_number(compression_info, context->compressed_rte->relid); Oid opno =
Oid opno = get_opfamily_member(tce->btree_opf, tce->type_id, tce->type_id, strategy); get_opfamily_member(tce->btree_opf, tce->type_id, tce->type_id, strategy);
if (!OidIsValid(opno)) if (!OidIsValid(opno))
return NULL; return NULL;
return (Expr *) make_segment_meta_opexpr(context, return (Expr *)
opno, make_segment_meta_opexpr(context,
meta_attno, opno,
var_with_segment_meta, get_segment_meta_max_attr_number(compression_info,
expr, context
strategy); ->compressed_rte
} ->relid),
var_with_segment_meta,
expr,
strategy);
}
default: default:
return NULL; return NULL;
} }

View File

@ -115,11 +115,6 @@ CrossModuleFunctions tsl_cm_functions = {
.process_compress_table = tsl_process_compress_table, .process_compress_table = tsl_process_compress_table,
.compress_chunk = tsl_compress_chunk, .compress_chunk = tsl_compress_chunk,
.decompress_chunk = tsl_decompress_chunk, .decompress_chunk = tsl_decompress_chunk,
.segment_meta_min_max_send = tsl_segment_meta_min_max_send,
.segment_meta_min_max_recv = tsl_segment_meta_min_max_recv,
.segment_meta_get_min = tsl_segment_meta_get_min,
.segment_meta_get_max = tsl_segment_meta_get_max,
.segment_meta_has_null = tsl_segment_meta_has_null,
}; };
TS_FUNCTION_INFO_V1(ts_module_init); TS_FUNCTION_INFO_V1(ts_module_init);

View File

@ -51,8 +51,10 @@ CREATE TABLE uncompressed(
CREATE TABLE compressed( CREATE TABLE compressed(
_ts_meta_count int, _ts_meta_count int,
_ts_meta_sequence_num int, _ts_meta_sequence_num int,
_ts_meta_min_max_1 _timescaledb_internal.segment_meta_min_max, _ts_meta_min_1 int,
_ts_meta_min_max_2 _timescaledb_internal.segment_meta_min_max, _ts_meta_max_1 int,
_ts_meta_min_2 int,
_ts_meta_max_2 int,
time _timescaledb_internal.compressed_data, time _timescaledb_internal.compressed_data,
device INT, device INT,
data _timescaledb_internal.compressed_data, data _timescaledb_internal.compressed_data,
@ -398,7 +400,8 @@ CREATE TABLE uncompressed(
CREATE TABLE compressed( CREATE TABLE compressed(
_ts_meta_count int, _ts_meta_count int,
_ts_meta_sequence_num int, _ts_meta_sequence_num int,
_ts_meta_min_max_1 _timescaledb_internal.segment_meta_min_max, _ts_meta_min_1 smallint,
_ts_meta_max_1 smallint,
b _timescaledb_internal.compressed_data, b _timescaledb_internal.compressed_data,
device _timescaledb_internal.compressed_data, device _timescaledb_internal.compressed_data,
time _timescaledb_internal.compressed_data); time _timescaledb_internal.compressed_data);

View File

@ -33,10 +33,11 @@ NOTICE: adding not-null constraint to column "a"
(1 row) (1 row)
create unique index foo_uniq ON foo (a, b); create unique index foo_uniq ON foo (a, b);
insert into foo values( 3 , 16 , 20, 11); --note that the "d" order by column is all NULL
insert into foo values( 10 , 10 , 20, 120); insert into foo values( 3 , 16 , 20, NULL);
insert into foo values( 20 , 11 , 20, 13); insert into foo values( 10 , 10 , 20, NULL);
insert into foo values( 30 , 12 , 20, 14); insert into foo values( 20 , 11 , 20, NULL);
insert into foo values( 30 , 12 , 20, NULL);
alter table foo set (timescaledb.compress, timescaledb.compress_segmentby = 'a,b', timescaledb.compress_orderby = 'c desc, d asc nulls last'); alter table foo set (timescaledb.compress, timescaledb.compress_segmentby = 'a,b', timescaledb.compress_orderby = 'c desc, d asc nulls last');
NOTICE: adding index _compressed_hypertable_2_a__ts_meta_sequence_num_idx ON _timescaledb_internal._compressed_hypertable_2 USING BTREE(a, _ts_meta_sequence_num) NOTICE: adding index _compressed_hypertable_2_a__ts_meta_sequence_num_idx ON _timescaledb_internal._compressed_hypertable_2 USING BTREE(a, _ts_meta_sequence_num)
NOTICE: adding index _compressed_hypertable_2_b__ts_meta_sequence_num_idx ON _timescaledb_internal._compressed_hypertable_2 USING BTREE(b, _ts_meta_sequence_num) NOTICE: adding index _compressed_hypertable_2_b__ts_meta_sequence_num_idx ON _timescaledb_internal._compressed_hypertable_2 USING BTREE(b, _ts_meta_sequence_num)
@ -65,7 +66,7 @@ select compress_chunk( '_timescaledb_internal._hyper_1_2_chunk');
(1 row) (1 row)
select tgname , tgtype, tgenabled , relname select tgname , tgtype, tgenabled , relname
from pg_trigger t, pg_class rel from pg_trigger t, pg_class rel
where t.tgrelid = rel.oid and rel.relname like '_hyper_1_2_chunk' order by tgname; where t.tgrelid = rel.oid and rel.relname like '_hyper_1_2_chunk' order by tgname;
tgname | tgtype | tgenabled | relname tgname | tgtype | tgenabled | relname
---------------------------------+--------+-----------+------------------ ---------------------------------+--------+-----------+------------------
@ -144,7 +145,7 @@ where ch1.compressed_chunk_id = ch2.id;
--cannot recompress the chunk the second time around --cannot recompress the chunk the second time around
select compress_chunk( '_timescaledb_internal._hyper_1_2_chunk'); select compress_chunk( '_timescaledb_internal._hyper_1_2_chunk');
ERROR: chunk is already compressed ERROR: chunk is already compressed
--TEST2a try DML on a compressed chunk --TEST2a try DML on a compressed chunk
insert into foo values( 11 , 10 , 20, 120); insert into foo values( 11 , 10 , 20, 120);
ERROR: insert/update/delete not permitted on chunk "_hyper_1_2_chunk" ERROR: insert/update/delete not permitted on chunk "_hyper_1_2_chunk"
update foo set b =20 where a = 10; update foo set b =20 where a = 10;
@ -196,7 +197,6 @@ ERROR: insert/update/delete not permitted on chunk "_hyper_1_2_chunk"
update _timescaledb_internal._hyper_1_2_chunk update _timescaledb_internal._hyper_1_2_chunk
set b = 12; set b = 12;
delete from _timescaledb_internal._hyper_1_2_chunk; delete from _timescaledb_internal._hyper_1_2_chunk;
--TEST2d decompress the chunk and try DML --TEST2d decompress the chunk and try DML
select decompress_chunk( '_timescaledb_internal._hyper_1_2_chunk'); select decompress_chunk( '_timescaledb_internal._hyper_1_2_chunk');
decompress_chunk decompress_chunk
@ -209,7 +209,7 @@ update foo set b =20 where a = 10;
select * from _timescaledb_internal._hyper_1_2_chunk order by a; select * from _timescaledb_internal._hyper_1_2_chunk order by a;
a | b | c | d a | b | c | d
----+----+----+----- ----+----+----+-----
10 | 20 | 20 | 120 10 | 20 | 20 |
11 | 10 | 20 | 120 11 | 10 | 20 | 120
(2 rows) (2 rows)
@ -414,8 +414,8 @@ SELECT count(*) from :COMPRESSED_CHUNK_NAME;
ERROR: relation "_timescaledb_internal.compress_hyper_6_14_chunk" does not exist at character 22 ERROR: relation "_timescaledb_internal.compress_hyper_6_14_chunk" does not exist at character 22
\set ON_ERROR_STOP 1 \set ON_ERROR_STOP 1
--size information is gone too --size information is gone too
select count(*) select count(*)
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht, FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht,
_timescaledb_catalog.compression_chunk_size map _timescaledb_catalog.compression_chunk_size map
where ch1.hypertable_id = ht.id and ht.table_name like 'conditions' where ch1.hypertable_id = ht.id and ht.table_name like 'conditions'
and map.chunk_id = ch1.id; and map.chunk_id = ch1.id;
@ -595,17 +595,17 @@ ERROR: could not determine which collation to use for string comparison
--segment meta on order bys pushdown --segment meta on order bys pushdown
--should work --should work
EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_1 < 'a'; EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_1 < 'a';
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------
Append Append
-> Custom Scan (DecompressChunk) on _hyper_9_19_chunk -> Custom Scan (DecompressChunk) on _hyper_9_19_chunk
Filter: (val_1 < 'a'::text) Filter: (val_1 < 'a'::text)
-> Seq Scan on compress_hyper_10_29_chunk -> Seq Scan on compress_hyper_10_29_chunk
Filter: (_timescaledb_internal.segment_meta_get_min(_ts_meta_min_max_1, NULL::text) < 'a'::text) Filter: (_ts_meta_min_1 < 'a'::text)
-> Custom Scan (DecompressChunk) on _hyper_9_20_chunk -> Custom Scan (DecompressChunk) on _hyper_9_20_chunk
Filter: (val_1 < 'a'::text) Filter: (val_1 < 'a'::text)
-> Seq Scan on compress_hyper_10_30_chunk -> Seq Scan on compress_hyper_10_30_chunk
Filter: (_timescaledb_internal.segment_meta_get_min(_ts_meta_min_max_1, NULL::text) < 'a'::text) Filter: (_ts_meta_min_1 < 'a'::text)
-> Seq Scan on _hyper_9_21_chunk -> Seq Scan on _hyper_9_21_chunk
Filter: (val_1 < 'a'::text) Filter: (val_1 < 'a'::text)
-> Seq Scan on _hyper_9_22_chunk -> Seq Scan on _hyper_9_22_chunk
@ -625,17 +625,17 @@ EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_1 < 'a';
(25 rows) (25 rows)
EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_2 < 'a'; EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_2 < 'a';
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------
Append Append
-> Custom Scan (DecompressChunk) on _hyper_9_19_chunk -> Custom Scan (DecompressChunk) on _hyper_9_19_chunk
Filter: (val_2 < 'a'::text) Filter: (val_2 < 'a'::text)
-> Seq Scan on compress_hyper_10_29_chunk -> Seq Scan on compress_hyper_10_29_chunk
Filter: (_timescaledb_internal.segment_meta_get_min(_ts_meta_min_max_2, NULL::text) < 'a'::text) Filter: (_ts_meta_min_2 < 'a'::text)
-> Custom Scan (DecompressChunk) on _hyper_9_20_chunk -> Custom Scan (DecompressChunk) on _hyper_9_20_chunk
Filter: (val_2 < 'a'::text) Filter: (val_2 < 'a'::text)
-> Seq Scan on compress_hyper_10_30_chunk -> Seq Scan on compress_hyper_10_30_chunk
Filter: (_timescaledb_internal.segment_meta_get_min(_ts_meta_min_max_2, NULL::text) < 'a'::text) Filter: (_ts_meta_min_2 < 'a'::text)
-> Seq Scan on _hyper_9_21_chunk -> Seq Scan on _hyper_9_21_chunk
Filter: (val_2 < 'a'::text) Filter: (val_2 < 'a'::text)
-> Seq Scan on _hyper_9_22_chunk -> Seq Scan on _hyper_9_22_chunk
@ -655,17 +655,17 @@ EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_2 < 'a';
(25 rows) (25 rows)
EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_1 < 'a' COLLATE "C"; EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_1 < 'a' COLLATE "C";
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------
Append Append
-> Custom Scan (DecompressChunk) on _hyper_9_19_chunk -> Custom Scan (DecompressChunk) on _hyper_9_19_chunk
Filter: (val_1 < 'a'::text COLLATE "C") Filter: (val_1 < 'a'::text COLLATE "C")
-> Seq Scan on compress_hyper_10_29_chunk -> Seq Scan on compress_hyper_10_29_chunk
Filter: (_timescaledb_internal.segment_meta_get_min(_ts_meta_min_max_1, NULL::text) < 'a'::text COLLATE "C") Filter: (_ts_meta_min_1 < 'a'::text COLLATE "C")
-> Custom Scan (DecompressChunk) on _hyper_9_20_chunk -> Custom Scan (DecompressChunk) on _hyper_9_20_chunk
Filter: (val_1 < 'a'::text COLLATE "C") Filter: (val_1 < 'a'::text COLLATE "C")
-> Seq Scan on compress_hyper_10_30_chunk -> Seq Scan on compress_hyper_10_30_chunk
Filter: (_timescaledb_internal.segment_meta_get_min(_ts_meta_min_max_1, NULL::text) < 'a'::text COLLATE "C") Filter: (_ts_meta_min_1 < 'a'::text COLLATE "C")
-> Seq Scan on _hyper_9_21_chunk -> Seq Scan on _hyper_9_21_chunk
Filter: (val_1 < 'a'::text COLLATE "C") Filter: (val_1 < 'a'::text COLLATE "C")
-> Seq Scan on _hyper_9_22_chunk -> Seq Scan on _hyper_9_22_chunk
@ -685,17 +685,17 @@ EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_1 < 'a' COLLATE "C";
(25 rows) (25 rows)
EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_2 < 'a' COLLATE "POSIX"; EXPLAIN (costs off) SELECT * FROM test_collation WHERE val_2 < 'a' COLLATE "POSIX";
QUERY PLAN QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------
Append Append
-> Custom Scan (DecompressChunk) on _hyper_9_19_chunk -> Custom Scan (DecompressChunk) on _hyper_9_19_chunk
Filter: (val_2 < 'a'::text COLLATE "POSIX") Filter: (val_2 < 'a'::text COLLATE "POSIX")
-> Seq Scan on compress_hyper_10_29_chunk -> Seq Scan on compress_hyper_10_29_chunk
Filter: (_timescaledb_internal.segment_meta_get_min(_ts_meta_min_max_2, NULL::text) < 'a'::text COLLATE "POSIX") Filter: (_ts_meta_min_2 < 'a'::text COLLATE "POSIX")
-> Custom Scan (DecompressChunk) on _hyper_9_20_chunk -> Custom Scan (DecompressChunk) on _hyper_9_20_chunk
Filter: (val_2 < 'a'::text COLLATE "POSIX") Filter: (val_2 < 'a'::text COLLATE "POSIX")
-> Seq Scan on compress_hyper_10_30_chunk -> Seq Scan on compress_hyper_10_30_chunk
Filter: (_timescaledb_internal.segment_meta_get_min(_ts_meta_min_max_2, NULL::text) < 'a'::text COLLATE "POSIX") Filter: (_ts_meta_min_2 < 'a'::text COLLATE "POSIX")
-> Seq Scan on _hyper_9_21_chunk -> Seq Scan on _hyper_9_21_chunk
Filter: (val_2 < 'a'::text COLLATE "POSIX") Filter: (val_2 < 'a'::text COLLATE "POSIX")
-> Seq Scan on _hyper_9_22_chunk -> Seq Scan on _hyper_9_22_chunk

View File

@ -67,7 +67,7 @@ NOTICE: adding not-null constraint to column "a"
ALTER TABLE reserved_column_prefix set (timescaledb.compress); ALTER TABLE reserved_column_prefix set (timescaledb.compress);
ERROR: cannot compress tables with reserved column prefix '_ts_meta_' ERROR: cannot compress tables with reserved column prefix '_ts_meta_'
--basic test with count --basic test with count
create table foo (a integer, b integer, c integer, t text); create table foo (a integer, b integer, c integer, t text, p point);
select table_name from create_hypertable('foo', 'a', chunk_time_interval=> 10); select table_name from create_hypertable('foo', 'a', chunk_time_interval=> 10);
NOTICE: adding not-null constraint to column "a" NOTICE: adding not-null constraint to column "a"
table_name table_name
@ -133,6 +133,8 @@ ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c L
ERROR: unable to parse the compress_segmentby option 'c LIMIT 1' ERROR: unable to parse the compress_segmentby option 'c LIMIT 1'
ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c + b'); ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c + b');
ERROR: unable to parse the compress_segmentby option 'c + b' ERROR: unable to parse the compress_segmentby option 'c + b'
ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'a, p');
ERROR: invalid order by column type: could not identify an less-than operator for type point
--should succeed --should succeed
ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'a, b'); ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'a, b');
--note that the time column "a" should not be added to the end of the order by list again (should appear first) --note that the time column "a" should not be added to the end of the order by list again (should appear first)
@ -142,8 +144,9 @@ select hc.* from _timescaledb_catalog.hypertable_compression hc inner join _time
8 | a | 4 | | 1 | t | f 8 | a | 4 | | 1 | t | f
8 | b | 4 | | 2 | t | f 8 | b | 4 | | 2 | t | f
8 | c | 4 | | | | 8 | c | 4 | | | |
8 | p | 1 | | | |
8 | t | 2 | | | | 8 | t | 2 | | | |
(4 rows) (5 rows)
select decompress_chunk(ch1.schema_name|| '.' || ch1.table_name) select decompress_chunk(ch1.schema_name|| '.' || ch1.table_name)
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht where ch1.hypertable_id = ht.id and ht.table_name like 'foo' ORDER BY ch1.id limit 1; FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht where ch1.hypertable_id = ht.id and ht.table_name like 'foo' ORDER BY ch1.id limit 1;
@ -187,8 +190,9 @@ select hc.* from _timescaledb_catalog.hypertable_compression hc inner join _time
8 | a | 4 | | 1 | t | f 8 | a | 4 | | 1 | t | f
8 | b | 0 | 1 | | | 8 | b | 0 | 1 | | |
8 | c | 4 | | | | 8 | c | 4 | | | |
8 | p | 1 | | | |
8 | t | 2 | | | | 8 | t | 2 | | | |
(4 rows) (5 rows)
SELECT comp_hyper.schema_name|| '.' || comp_hyper.table_name as "COMPRESSED_HYPER_NAME" SELECT comp_hyper.schema_name|| '.' || comp_hyper.table_name as "COMPRESSED_HYPER_NAME"
FROM _timescaledb_catalog.hypertable comp_hyper FROM _timescaledb_catalog.hypertable comp_hyper

View File

@ -79,7 +79,8 @@ pg_dump: Consider using a full dump instead of a --data-only dump to avoid this
\set TYPE timestamptz \set TYPE timestamptz
\set ORDER_BY_COL_NAME Time \set ORDER_BY_COL_NAME Time
\set SEGMENT_META_COL _ts_meta_min_max_1 \set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql \ir include/compression_test_hypertable_segment_meta.sql
-- This file and its contents are licensed under the Timescale License. -- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
@ -90,9 +91,9 @@ pg_dump: Consider using a full dump instead of a --data-only dump to avoid this
27 27
(1 row) (1 row)
min_correct | max_correct | has_null_correct min_correct | max_correct
-------------+-------------+------------------ -------------+-------------
t | t | t t | t
(1 row) (1 row)
TRUNCATE test1; TRUNCATE test1;
@ -104,8 +105,8 @@ SELECT * FROM test1;
/* nor compressed table */ /* nor compressed table */
SELECT * FROM _timescaledb_internal._compressed_hypertable_2; SELECT * FROM _timescaledb_internal._compressed_hypertable_2;
Time | i | b | t | _ts_meta_count | _ts_meta_sequence_num | _ts_meta_min_max_1 Time | i | b | t | _ts_meta_count | _ts_meta_sequence_num | _ts_meta_min_1 | _ts_meta_max_1
------+---+---+---+----------------+-----------------------+-------------------- ------+---+---+---+----------------+-----------------------+----------------+----------------
(0 rows) (0 rows)
/* the compressed table should have not chunks */ /* the compressed table should have not chunks */
@ -175,7 +176,8 @@ pg_dump: Consider using a full dump instead of a --data-only dump to avoid this
\set TYPE int \set TYPE int
\set ORDER_BY_COL_NAME c \set ORDER_BY_COL_NAME c
\set SEGMENT_META_COL _ts_meta_min_max_1 \set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql \ir include/compression_test_hypertable_segment_meta.sql
-- This file and its contents are licensed under the Timescale License. -- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
@ -186,14 +188,15 @@ pg_dump: Consider using a full dump instead of a --data-only dump to avoid this
5 5
(1 row) (1 row)
min_correct | max_correct | has_null_correct min_correct | max_correct
-------------+-------------+------------------ -------------+-------------
t | t | t t | t
(1 row) (1 row)
\set TYPE timestamptz \set TYPE timestamptz
\set ORDER_BY_COL_NAME Time \set ORDER_BY_COL_NAME Time
\set SEGMENT_META_COL _ts_meta_min_max_2 \set SEGMENT_META_COL_MIN _ts_meta_min_2
\set SEGMENT_META_COL_MAX _ts_meta_max_2
\ir include/compression_test_hypertable_segment_meta.sql \ir include/compression_test_hypertable_segment_meta.sql
-- This file and its contents are licensed under the Timescale License. -- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
@ -204,9 +207,9 @@ pg_dump: Consider using a full dump instead of a --data-only dump to avoid this
0 0
(1 row) (1 row)
min_correct | max_correct | has_null_correct min_correct | max_correct
-------------+-------------+------------------ -------------+-------------
t | t | t t | t
(1 row) (1 row)
--TEST4 create segments with > 1000 rows. --TEST4 create segments with > 1000 rows.
@ -284,7 +287,8 @@ pg_dump: Consider using a full dump instead of a --data-only dump to avoid this
\set TYPE TIMESTAMPTZ \set TYPE TIMESTAMPTZ
\set ORDER_BY_COL_NAME timec \set ORDER_BY_COL_NAME timec
\set SEGMENT_META_COL _ts_meta_min_max_1 \set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql \ir include/compression_test_hypertable_segment_meta.sql
-- This file and its contents are licensed under the Timescale License. -- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
@ -295,9 +299,9 @@ pg_dump: Consider using a full dump instead of a --data-only dump to avoid this
1 1
(1 row) (1 row)
min_correct | max_correct | has_null_correct min_correct | max_correct
-------------+-------------+------------------ -------------+-------------
t | t | t t | t
(1 row) (1 row)
--add hypertable with order by a non by-val type with NULLs --add hypertable with order by a non by-val type with NULLs
@ -357,7 +361,8 @@ pg_dump: Consider using a full dump instead of a --data-only dump to avoid this
\set TYPE TEXT \set TYPE TEXT
\set ORDER_BY_COL_NAME device_id \set ORDER_BY_COL_NAME device_id
\set SEGMENT_META_COL _ts_meta_min_max_1 \set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql \ir include/compression_test_hypertable_segment_meta.sql
-- This file and its contents are licensed under the Timescale License. -- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
@ -368,9 +373,9 @@ pg_dump: Consider using a full dump instead of a --data-only dump to avoid this
10 10
(1 row) (1 row)
min_correct | max_correct | has_null_correct min_correct | max_correct
-------------+-------------+------------------ -------------+-------------
t | t | t t | t
(1 row) (1 row)
TRUNCATE test5; TRUNCATE test5;

View File

@ -6,14 +6,25 @@ CREATE OR REPLACE FUNCTION _timescaledb_internal.tsl_segment_meta_min_max_append
RETURNS internal RETURNS internal
AS :TSL_MODULE_PATHNAME, 'tsl_segment_meta_min_max_append' AS :TSL_MODULE_PATHNAME, 'tsl_segment_meta_min_max_append'
LANGUAGE C IMMUTABLE PARALLEL SAFE; LANGUAGE C IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION _timescaledb_internal.tsl_segment_meta_min_max_finish(internal) CREATE OR REPLACE FUNCTION _timescaledb_internal.tsl_segment_meta_min_max_finish_max(internal, ANYELEMENT)
RETURNS _timescaledb_internal.segment_meta_min_max RETURNS anyelement
AS :TSL_MODULE_PATHNAME, 'tsl_segment_meta_min_max_finish' AS :TSL_MODULE_PATHNAME, 'tsl_segment_meta_min_max_finish_max'
LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT; LANGUAGE C IMMUTABLE PARALLEL SAFE;
CREATE AGGREGATE _timescaledb_internal.segment_meta_min_max_agg(ANYELEMENT) ( CREATE OR REPLACE FUNCTION _timescaledb_internal.tsl_segment_meta_min_max_finish_min(internal, ANYELEMENT)
RETURNS anyelement
AS :TSL_MODULE_PATHNAME, 'tsl_segment_meta_min_max_finish_min'
LANGUAGE C IMMUTABLE PARALLEL SAFE;
CREATE AGGREGATE _timescaledb_internal.segment_meta_min_max_agg_min(ANYELEMENT) (
STYPE = internal, STYPE = internal,
SFUNC = _timescaledb_internal.tsl_segment_meta_min_max_append, SFUNC = _timescaledb_internal.tsl_segment_meta_min_max_append,
FINALFUNC = _timescaledb_internal.tsl_segment_meta_min_max_finish FINALFUNC = _timescaledb_internal.tsl_segment_meta_min_max_finish_min,
FINALFUNC_EXTRA
);
CREATE AGGREGATE _timescaledb_internal.segment_meta_min_max_agg_max(ANYELEMENT) (
STYPE = internal,
SFUNC = _timescaledb_internal.tsl_segment_meta_min_max_append,
FINALFUNC = _timescaledb_internal.tsl_segment_meta_min_max_finish_max,
FINALFUNC_EXTRA
); );
\ir include/rand_generator.sql \ir include/rand_generator.sql
-- This file and its contents are licensed under the Timescale License. -- This file and its contents are licensed under the Timescale License.
@ -35,42 +46,9 @@ $$
$$; $$;
-- seed the random num generator -- seed the random num generator
insert into rand_minstd_state values (321); insert into rand_minstd_state values (321);
--use a custom type without send and recv functions to test
--the input/output fallback path.
CREATE TYPE customtype_no_send_recv;
CREATE OR REPLACE FUNCTION customtype_in(cstring) RETURNS customtype_no_send_recv AS
'timestamptz_in'
LANGUAGE internal IMMUTABLE STRICT;
NOTICE: return type customtype_no_send_recv is only a shell
CREATE OR REPLACE FUNCTION customtype_out( customtype_no_send_recv) RETURNS cstring AS
'timestamptz_out'
LANGUAGE internal IMMUTABLE STRICT;
NOTICE: argument type customtype_no_send_recv is only a shell
CREATE TYPE customtype_no_send_recv (
INPUT = customtype_in,
OUTPUT = customtype_out,
LIKE = TIMESTAMPTZ
);
CREATE CAST (customtype_no_send_recv AS bigint)
WITHOUT FUNCTION AS IMPLICIT;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER \c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
CREATE TABLE metric (i int); CREATE TABLE metric (i int);
insert into metric select i from generate_series(1, 10) i; insert into metric select i from generate_series(1, 10) i;
SELECT
_timescaledb_internal.segment_meta_get_min(meta, NULL::int),
_timescaledb_internal.segment_meta_get_max(meta, NULL::int),
_timescaledb_internal.segment_meta_has_null(meta)
FROM
(
SELECT
_timescaledb_internal.segment_meta_min_max_agg(i) as meta
FROM metric
) AS meta_gen;
segment_meta_get_min | segment_meta_get_max | segment_meta_has_null
----------------------+----------------------+-----------------------
1 | 10 | f
(1 row)
\set TYPE int \set TYPE int
\set TABLE metric \set TABLE metric
\ir include/compression_test_segment_meta.sql \ir include/compression_test_segment_meta.sql
@ -78,9 +56,9 @@ FROM
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license. -- LICENSE-TIMESCALE for a copy of the license.
\set ECHO errors \set ECHO errors
min_correct | max_correct | has_null_correct ?column? | ?column?
-------------+-------------+------------------ ----------+----------
t | t | t t | t
(1 row) (1 row)
----NULL tests ----NULL tests
@ -92,9 +70,9 @@ insert into metric select NULLIF(i,1) from generate_series(1, 10) i;
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license. -- LICENSE-TIMESCALE for a copy of the license.
\set ECHO errors \set ECHO errors
min_correct | max_correct | has_null_correct ?column? | ?column?
-------------+-------------+------------------ ----------+----------
t | t | t t | t
(1 row) (1 row)
--Last --Last
@ -105,9 +83,9 @@ insert into metric select NULLIF(i,10) from generate_series(1, 10) i;
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license. -- LICENSE-TIMESCALE for a copy of the license.
\set ECHO errors \set ECHO errors
min_correct | max_correct | has_null_correct ?column? | ?column?
-------------+-------------+------------------ ----------+----------
t | t | t t | t
(1 row) (1 row)
--Middle --Middle
@ -118,33 +96,23 @@ insert into metric select NULLIF(i,5) from generate_series(1, 10) i;
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license. -- LICENSE-TIMESCALE for a copy of the license.
\set ECHO errors \set ECHO errors
min_correct | max_correct | has_null_correct ?column? | ?column?
-------------+-------------+------------------ ----------+----------
t | t | t t | t
(1 row) (1 row)
--All NULLS should return null object --All NULLS should return null object
truncate metric; truncate metric;
insert into metric select NULL from generate_series(1, 10) i; insert into metric select NULL from generate_series(1, 10) i;
SELECT SELECT
_timescaledb_internal.segment_meta_min_max_agg(i) is NULL, _timescaledb_internal.segment_meta_min_max_agg_min(i) is null,
_timescaledb_internal.segment_meta_min_max_agg(i)::text is NULL _timescaledb_internal.segment_meta_min_max_agg_max(i) is null
FROM metric; FROM metric;
?column? | ?column? ?column? | ?column?
----------+---------- ----------+----------
t | t t | t
(1 row) (1 row)
--accessor functions work on NULLs
SELECT
_timescaledb_internal.segment_meta_get_min(NULL, NULL::int) IS NULL,
_timescaledb_internal.segment_meta_get_max(NULL, NULL::int) IS NULL,
_timescaledb_internal.segment_meta_has_null(NULL);
?column? | ?column? | segment_meta_has_null
----------+----------+-----------------------
t | t | t
(1 row)
-- --
--type tests --type tests
-- --
@ -163,9 +131,9 @@ CREATE TABLE base_texts AS SELECT
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license. -- LICENSE-TIMESCALE for a copy of the license.
\set ECHO errors \set ECHO errors
min_correct | max_correct | has_null_correct ?column? | ?column?
-------------+-------------+------------------ ----------+----------
t | t | t t | t
(1 row) (1 row)
--toasted text --toasted text
@ -191,9 +159,9 @@ SELECT pg_total_relation_size(reltoastrelid)
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license. -- LICENSE-TIMESCALE for a copy of the license.
\set ECHO errors \set ECHO errors
min_correct | max_correct | has_null_correct ?column? | ?column?
-------------+-------------+------------------ ----------+----------
t | t | t t | t
(1 row) (1 row)
--name is a fixed-length pass by reference type --name is a fixed-length pass by reference type
@ -211,9 +179,9 @@ CREATE TABLE base_name AS SELECT
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license. -- LICENSE-TIMESCALE for a copy of the license.
\set ECHO errors \set ECHO errors
min_correct | max_correct | has_null_correct ?column? | ?column?
-------------+-------------+------------------ ----------+----------
t | t | t t | t
(1 row) (1 row)
--array --array
@ -231,9 +199,9 @@ CREATE TABLE text_array AS SELECT
-- Please see the included NOTICE for copyright information and -- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license. -- LICENSE-TIMESCALE for a copy of the license.
\set ECHO errors \set ECHO errors
min_correct | max_correct | has_null_correct ?column? | ?column?
-------------+-------------+------------------ ----------+----------
t | t | t t | t
(1 row) (1 row)
--Points doesn't have an ordering so make sure it errors --Points doesn't have an ordering so make sure it errors
@ -246,27 +214,11 @@ CREATE TABLE points AS SELECT
) sub; ) sub;
\set ON_ERROR_STOP 0 \set ON_ERROR_STOP 0
SELECT SELECT
_timescaledb_internal.segment_meta_min_max_agg(i) _timescaledb_internal.segment_meta_min_max_agg_max(i)
FROM points;
ERROR: could not identify an less-than operator for type point
SELECT
_timescaledb_internal.segment_meta_min_max_agg_min(i)
FROM points; FROM points;
ERROR: could not identify an less-than operator for type point ERROR: could not identify an less-than operator for type point
\set ON_ERROR_STOP 1 \set ON_ERROR_STOP 1
--test with a custom type with no send/recv
CREATE TABLE customtype_table AS SELECT
item::text::customtype_no_send_recv as i
FROM
(SELECT sub.item from
(SELECT generate_series('2001-01-01 01:01:01', '2001-01-02 01:01:01', INTERVAL '1 hour') item) as sub
ORDER BY gen_rand_minstd()
) sub;
\set TYPE customtype_no_send_recv
\set TABLE customtype_table
\ir include/compression_test_segment_meta.sql
-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
\set ECHO errors
min_correct | max_correct | has_null_correct
-------------+-------------+------------------
t | t | t
(1 row)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -50,8 +50,10 @@ CREATE TABLE uncompressed(
CREATE TABLE compressed( CREATE TABLE compressed(
_ts_meta_count int, _ts_meta_count int,
_ts_meta_sequence_num int, _ts_meta_sequence_num int,
_ts_meta_min_max_1 _timescaledb_internal.segment_meta_min_max, _ts_meta_min_1 int,
_ts_meta_min_max_2 _timescaledb_internal.segment_meta_min_max, _ts_meta_max_1 int,
_ts_meta_min_2 int,
_ts_meta_max_2 int,
time _timescaledb_internal.compressed_data, time _timescaledb_internal.compressed_data,
device INT, device INT,
data _timescaledb_internal.compressed_data, data _timescaledb_internal.compressed_data,
@ -177,7 +179,8 @@ CREATE TABLE uncompressed(
CREATE TABLE compressed( CREATE TABLE compressed(
_ts_meta_count int, _ts_meta_count int,
_ts_meta_sequence_num int, _ts_meta_sequence_num int,
_ts_meta_min_max_1 _timescaledb_internal.segment_meta_min_max, _ts_meta_min_1 smallint,
_ts_meta_max_1 smallint,
b _timescaledb_internal.compressed_data, b _timescaledb_internal.compressed_data,
device _timescaledb_internal.compressed_data, device _timescaledb_internal.compressed_data,
time _timescaledb_internal.compressed_data); time _timescaledb_internal.compressed_data);

View File

@ -12,10 +12,11 @@ create table foo (a integer, b integer, c integer, d integer);
select table_name from create_hypertable('foo', 'a', chunk_time_interval=> 10); select table_name from create_hypertable('foo', 'a', chunk_time_interval=> 10);
create unique index foo_uniq ON foo (a, b); create unique index foo_uniq ON foo (a, b);
insert into foo values( 3 , 16 , 20, 11); --note that the "d" order by column is all NULL
insert into foo values( 10 , 10 , 20, 120); insert into foo values( 3 , 16 , 20, NULL);
insert into foo values( 20 , 11 , 20, 13); insert into foo values( 10 , 10 , 20, NULL);
insert into foo values( 30 , 12 , 20, 14); insert into foo values( 20 , 11 , 20, NULL);
insert into foo values( 30 , 12 , 20, NULL);
alter table foo set (timescaledb.compress, timescaledb.compress_segmentby = 'a,b', timescaledb.compress_orderby = 'c desc, d asc nulls last'); alter table foo set (timescaledb.compress, timescaledb.compress_segmentby = 'a,b', timescaledb.compress_orderby = 'c desc, d asc nulls last');
select id, schema_name, table_name, compressed, compressed_hypertable_id from select id, schema_name, table_name, compressed, compressed_hypertable_id from
@ -25,7 +26,7 @@ select * from _timescaledb_catalog.hypertable_compression order by hypertable_id
-- TEST2 compress-chunk for the chunks created earlier -- -- TEST2 compress-chunk for the chunks created earlier --
select compress_chunk( '_timescaledb_internal._hyper_1_2_chunk'); select compress_chunk( '_timescaledb_internal._hyper_1_2_chunk');
select tgname , tgtype, tgenabled , relname select tgname , tgtype, tgenabled , relname
from pg_trigger t, pg_class rel from pg_trigger t, pg_class rel
where t.tgrelid = rel.oid and rel.relname like '_hyper_1_2_chunk' order by tgname; where t.tgrelid = rel.oid and rel.relname like '_hyper_1_2_chunk' order by tgname;
\x \x
select * from timescaledb_information.compressed_chunk_stats select * from timescaledb_information.compressed_chunk_stats
@ -45,7 +46,7 @@ where ch1.compressed_chunk_id = ch2.id;
--cannot recompress the chunk the second time around --cannot recompress the chunk the second time around
select compress_chunk( '_timescaledb_internal._hyper_1_2_chunk'); select compress_chunk( '_timescaledb_internal._hyper_1_2_chunk');
--TEST2a try DML on a compressed chunk --TEST2a try DML on a compressed chunk
insert into foo values( 11 , 10 , 20, 120); insert into foo values( 11 , 10 , 20, 120);
update foo set b =20 where a = 10; update foo set b =20 where a = 10;
delete from foo where a = 10; delete from foo where a = 10;
@ -80,7 +81,7 @@ insert into _timescaledb_internal._hyper_1_2_chunk values(10, 12, 12, 12);
update _timescaledb_internal._hyper_1_2_chunk update _timescaledb_internal._hyper_1_2_chunk
set b = 12; set b = 12;
delete from _timescaledb_internal._hyper_1_2_chunk; delete from _timescaledb_internal._hyper_1_2_chunk;
--TEST2d decompress the chunk and try DML --TEST2d decompress the chunk and try DML
select decompress_chunk( '_timescaledb_internal._hyper_1_2_chunk'); select decompress_chunk( '_timescaledb_internal._hyper_1_2_chunk');
insert into foo values( 11 , 10 , 20, 120); insert into foo values( 11 , 10 , 20, 120);
@ -166,8 +167,8 @@ SELECT count(*) from :COMPRESSED_CHUNK_NAME;
\set ON_ERROR_STOP 1 \set ON_ERROR_STOP 1
--size information is gone too --size information is gone too
select count(*) select count(*)
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht, FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht,
_timescaledb_catalog.compression_chunk_size map _timescaledb_catalog.compression_chunk_size map
where ch1.hypertable_id = ht.id and ht.table_name like 'conditions' where ch1.hypertable_id = ht.id and ht.table_name like 'conditions'
and map.chunk_id = ch1.id; and map.chunk_id = ch1.id;

View File

@ -36,7 +36,7 @@ select table_name from create_hypertable('reserved_column_prefix', 'a', chunk_ti
ALTER TABLE reserved_column_prefix set (timescaledb.compress); ALTER TABLE reserved_column_prefix set (timescaledb.compress);
--basic test with count --basic test with count
create table foo (a integer, b integer, c integer, t text); create table foo (a integer, b integer, c integer, t text, p point);
select table_name from create_hypertable('foo', 'a', chunk_time_interval=> 10); select table_name from create_hypertable('foo', 'a', chunk_time_interval=> 10);
insert into foo values( 3 , 16 , 20); insert into foo values( 3 , 16 , 20);
@ -73,6 +73,7 @@ ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c +
ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'random()'); ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'random()');
ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c LIMIT 1'); ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c LIMIT 1');
ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c + b'); ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c + b');
ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'a, p');
--should succeed --should succeed
ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'a, b'); ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'a, b');

View File

@ -24,7 +24,8 @@ SELECT 'test1' AS "HYPERTABLE_NAME" \gset
\ir include/compression_test_hypertable.sql \ir include/compression_test_hypertable.sql
\set TYPE timestamptz \set TYPE timestamptz
\set ORDER_BY_COL_NAME Time \set ORDER_BY_COL_NAME Time
\set SEGMENT_META_COL _ts_meta_min_max_1 \set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql \ir include/compression_test_hypertable_segment_meta.sql
TRUNCATE test1; TRUNCATE test1;
@ -63,12 +64,14 @@ SELECT 'test2' AS "HYPERTABLE_NAME" \gset
\set TYPE int \set TYPE int
\set ORDER_BY_COL_NAME c \set ORDER_BY_COL_NAME c
\set SEGMENT_META_COL _ts_meta_min_max_1 \set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql \ir include/compression_test_hypertable_segment_meta.sql
\set TYPE timestamptz \set TYPE timestamptz
\set ORDER_BY_COL_NAME Time \set ORDER_BY_COL_NAME Time
\set SEGMENT_META_COL _ts_meta_min_max_2 \set SEGMENT_META_COL_MIN _ts_meta_min_2
\set SEGMENT_META_COL_MAX _ts_meta_max_2
\ir include/compression_test_hypertable_segment_meta.sql \ir include/compression_test_hypertable_segment_meta.sql
--TEST4 create segments with > 1000 rows. --TEST4 create segments with > 1000 rows.
@ -101,7 +104,8 @@ SELECT 'test4' AS "HYPERTABLE_NAME" \gset
\ir include/compression_test_hypertable.sql \ir include/compression_test_hypertable.sql
\set TYPE TIMESTAMPTZ \set TYPE TIMESTAMPTZ
\set ORDER_BY_COL_NAME timec \set ORDER_BY_COL_NAME timec
\set SEGMENT_META_COL _ts_meta_min_max_1 \set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql \ir include/compression_test_hypertable_segment_meta.sql
@ -131,7 +135,8 @@ SELECT 'test5' AS "HYPERTABLE_NAME" \gset
\ir include/compression_test_hypertable.sql \ir include/compression_test_hypertable.sql
\set TYPE TEXT \set TYPE TEXT
\set ORDER_BY_COL_NAME device_id \set ORDER_BY_COL_NAME device_id
\set SEGMENT_META_COL _ts_meta_min_max_1 \set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql \ir include/compression_test_hypertable_segment_meta.sql
TRUNCATE test5; TRUNCATE test5;

View File

@ -9,56 +9,37 @@ CREATE OR REPLACE FUNCTION _timescaledb_internal.tsl_segment_meta_min_max_append
AS :TSL_MODULE_PATHNAME, 'tsl_segment_meta_min_max_append' AS :TSL_MODULE_PATHNAME, 'tsl_segment_meta_min_max_append'
LANGUAGE C IMMUTABLE PARALLEL SAFE; LANGUAGE C IMMUTABLE PARALLEL SAFE;
CREATE OR REPLACE FUNCTION _timescaledb_internal.tsl_segment_meta_min_max_finish(internal) CREATE OR REPLACE FUNCTION _timescaledb_internal.tsl_segment_meta_min_max_finish_max(internal, ANYELEMENT)
RETURNS _timescaledb_internal.segment_meta_min_max RETURNS anyelement
AS :TSL_MODULE_PATHNAME, 'tsl_segment_meta_min_max_finish' AS :TSL_MODULE_PATHNAME, 'tsl_segment_meta_min_max_finish_max'
LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT; LANGUAGE C IMMUTABLE PARALLEL SAFE;
CREATE AGGREGATE _timescaledb_internal.segment_meta_min_max_agg(ANYELEMENT) ( CREATE OR REPLACE FUNCTION _timescaledb_internal.tsl_segment_meta_min_max_finish_min(internal, ANYELEMENT)
RETURNS anyelement
AS :TSL_MODULE_PATHNAME, 'tsl_segment_meta_min_max_finish_min'
LANGUAGE C IMMUTABLE PARALLEL SAFE;
CREATE AGGREGATE _timescaledb_internal.segment_meta_min_max_agg_min(ANYELEMENT) (
STYPE = internal, STYPE = internal,
SFUNC = _timescaledb_internal.tsl_segment_meta_min_max_append, SFUNC = _timescaledb_internal.tsl_segment_meta_min_max_append,
FINALFUNC = _timescaledb_internal.tsl_segment_meta_min_max_finish FINALFUNC = _timescaledb_internal.tsl_segment_meta_min_max_finish_min,
FINALFUNC_EXTRA
);
CREATE AGGREGATE _timescaledb_internal.segment_meta_min_max_agg_max(ANYELEMENT) (
STYPE = internal,
SFUNC = _timescaledb_internal.tsl_segment_meta_min_max_append,
FINALFUNC = _timescaledb_internal.tsl_segment_meta_min_max_finish_max,
FINALFUNC_EXTRA
); );
\ir include/rand_generator.sql \ir include/rand_generator.sql
--use a custom type without send and recv functions to test
--the input/output fallback path.
CREATE TYPE customtype_no_send_recv;
CREATE OR REPLACE FUNCTION customtype_in(cstring) RETURNS customtype_no_send_recv AS
'timestamptz_in'
LANGUAGE internal IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION customtype_out( customtype_no_send_recv) RETURNS cstring AS
'timestamptz_out'
LANGUAGE internal IMMUTABLE STRICT;
CREATE TYPE customtype_no_send_recv (
INPUT = customtype_in,
OUTPUT = customtype_out,
LIKE = TIMESTAMPTZ
);
CREATE CAST (customtype_no_send_recv AS bigint)
WITHOUT FUNCTION AS IMPLICIT;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER \c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
CREATE TABLE metric (i int); CREATE TABLE metric (i int);
insert into metric select i from generate_series(1, 10) i; insert into metric select i from generate_series(1, 10) i;
SELECT
_timescaledb_internal.segment_meta_get_min(meta, NULL::int),
_timescaledb_internal.segment_meta_get_max(meta, NULL::int),
_timescaledb_internal.segment_meta_has_null(meta)
FROM
(
SELECT
_timescaledb_internal.segment_meta_min_max_agg(i) as meta
FROM metric
) AS meta_gen;
\set TYPE int \set TYPE int
\set TABLE metric \set TABLE metric
\ir include/compression_test_segment_meta.sql \ir include/compression_test_segment_meta.sql
@ -82,20 +63,11 @@ insert into metric select NULLIF(i,5) from generate_series(1, 10) i;
--All NULLS should return null object --All NULLS should return null object
truncate metric; truncate metric;
insert into metric select NULL from generate_series(1, 10) i; insert into metric select NULL from generate_series(1, 10) i;
SELECT SELECT
_timescaledb_internal.segment_meta_min_max_agg(i) is NULL, _timescaledb_internal.segment_meta_min_max_agg_min(i) is null,
_timescaledb_internal.segment_meta_min_max_agg(i)::text is NULL _timescaledb_internal.segment_meta_min_max_agg_max(i) is null
FROM metric; FROM metric;
--accessor functions work on NULLs
SELECT
_timescaledb_internal.segment_meta_get_min(NULL, NULL::int) IS NULL,
_timescaledb_internal.segment_meta_get_max(NULL, NULL::int) IS NULL,
_timescaledb_internal.segment_meta_has_null(NULL);
-- --
--type tests --type tests
-- --
@ -168,20 +140,9 @@ CREATE TABLE points AS SELECT
\set ON_ERROR_STOP 0 \set ON_ERROR_STOP 0
SELECT SELECT
_timescaledb_internal.segment_meta_min_max_agg(i) _timescaledb_internal.segment_meta_min_max_agg_max(i)
FROM points;
SELECT
_timescaledb_internal.segment_meta_min_max_agg_min(i)
FROM points; FROM points;
\set ON_ERROR_STOP 1 \set ON_ERROR_STOP 1
--test with a custom type with no send/recv
CREATE TABLE customtype_table AS SELECT
item::text::customtype_no_send_recv as i
FROM
(SELECT sub.item from
(SELECT generate_series('2001-01-01 01:01:01', '2001-01-02 01:01:01', INTERVAL '1 hour') item) as sub
ORDER BY gen_rand_minstd()
) sub;
\set TYPE customtype_no_send_recv
\set TABLE customtype_table
\ir include/compression_test_segment_meta.sql

View File

@ -20,9 +20,8 @@ INNER JOIN _timescaledb_catalog.hypertable comp_hypertable ON (comp_hypertable.i
WHERE uc_hypertable.table_name like :'HYPERTABLE_NAME' \gset WHERE uc_hypertable.table_name like :'HYPERTABLE_NAME' \gset
SELECT SELECT
bool_and(_timescaledb_internal.segment_meta_get_min(:SEGMENT_META_COL, :NULLTYPE) = true_min) as min_correct, bool_and(:SEGMENT_META_COL_MIN = true_min) as min_correct,
bool_and(_timescaledb_internal.segment_meta_get_max(:SEGMENT_META_COL, :NULLTYPE) = true_max) as max_correct, bool_and(:SEGMENT_META_COL_MAX = true_max) as max_correct
bool_and(_timescaledb_internal.segment_meta_has_null(:SEGMENT_META_COL) = true_has_null) as has_null_correct
FROM FROM
:"COMP_SCHEMA_NAME".:"COMP_TABLE_NAME", LATERAL ( :"COMP_SCHEMA_NAME".:"COMP_TABLE_NAME", LATERAL (
SELECT min(decomp) true_min, max(decomp) true_max, ((count(*)-count(decomp)) > 0) true_has_null SELECT min(decomp) true_min, max(decomp) true_max, ((count(*)-count(decomp)) > 0) true_has_null

View File

@ -4,25 +4,9 @@
\set ECHO errors \set ECHO errors
SELECT 'NULL::'||:'TYPE' as "NULLTYPE" \gset
SELECT SELECT
_timescaledb_internal.segment_meta_min_max_agg(i)::text as "META_TEXT", _timescaledb_internal.segment_meta_min_max_agg_max(i) = max(i),
min(i) as "TRUE_MIN", _timescaledb_internal.segment_meta_min_max_agg_min(i) = min(i)
max(i) as "TRUE_MAX", FROM :"TABLE";
(count(*)-count(i)) > 0 as "TRUE_HAS_NULL"
FROM :"TABLE" \gset
SELECT
_timescaledb_internal.segment_meta_get_min(meta, :NULLTYPE) = :'TRUE_MIN' as min_correct,
_timescaledb_internal.segment_meta_get_max(meta, :NULLTYPE) = :'TRUE_MAX' as max_correct,
_timescaledb_internal.segment_meta_has_null(meta) = :'TRUE_HAS_NULL' as has_null_correct
FROM
(
SELECT
:'META_TEXT'::_timescaledb_internal.segment_meta_min_max as meta
) AS meta_gen;
\set ECHO all \set ECHO all

View File

@ -577,20 +577,28 @@ tsl_segment_meta_min_max_append(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(builder); PG_RETURN_POINTER(builder);
} }
TS_FUNCTION_INFO_V1(tsl_segment_meta_min_max_finish); TS_FUNCTION_INFO_V1(tsl_segment_meta_min_max_finish_max);
Datum Datum
tsl_segment_meta_min_max_finish(PG_FUNCTION_ARGS) tsl_segment_meta_min_max_finish_max(PG_FUNCTION_ARGS)
{ {
SegmentMetaMinMaxBuilder *builder = SegmentMetaMinMaxBuilder *builder =
(SegmentMetaMinMaxBuilder *) (PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0)); (SegmentMetaMinMaxBuilder *) (PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));
SegmentMetaMinMax *res;
if (builder == NULL) if (builder == NULL || segment_meta_min_max_builder_empty(builder))
PG_RETURN_NULL(); PG_RETURN_NULL();
res = segment_meta_min_max_builder_finish(builder); PG_RETURN_DATUM(segment_meta_min_max_builder_max(builder));
if (res == NULL) }
PG_RETURN_NULL();
TS_FUNCTION_INFO_V1(tsl_segment_meta_min_max_finish_min);
PG_RETURN_POINTER(res); Datum
tsl_segment_meta_min_max_finish_min(PG_FUNCTION_ARGS)
{
SegmentMetaMinMaxBuilder *builder =
(SegmentMetaMinMaxBuilder *) (PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));
if (builder == NULL || segment_meta_min_max_builder_empty(builder))
PG_RETURN_NULL();
PG_RETURN_DATUM(segment_meta_min_max_builder_min(builder));
} }