Make all partitioning functions take anyelement argument

All partitioning functions now has the signature `int func(anyelement)`.
This cleans up some special handling that was necessary to support
the legacy partitioning function that expected text input.
This commit is contained in:
Erik Nordström 2017-11-17 15:27:38 +01:00 committed by Erik Nordström
parent 0497744803
commit ca0968aaa0
12 changed files with 152 additions and 110 deletions

View File

@ -1,5 +1,5 @@
-- Deprecated partition hash function
CREATE OR REPLACE FUNCTION _timescaledb_internal.get_partition_for_key(val text)
CREATE OR REPLACE FUNCTION _timescaledb_internal.get_partition_for_key(val anyelement)
RETURNS int
AS '$libdir/timescaledb', 'get_partition_for_key' LANGUAGE C IMMUTABLE STRICT;

View File

@ -5,6 +5,9 @@ DECLARE
chunk_constraint_row _timescaledb_catalog.chunk_constraint;
chunk_row _timescaledb_catalog.chunk;
BEGIN
-- Need to do this update in two loops: first remove constraints, then add back.
-- This is because we can only remove the old partitioning function when
-- there are no constraints on the tables referencing the old function
FOR chunk_constraint_row IN
SELECT cc.*
FROM _timescaledb_catalog.chunk_constraint cc
@ -15,7 +18,19 @@ BEGIN
SELECT * INTO STRICT chunk_row FROM _timescaledb_catalog.chunk c WHERE c.id = chunk_constraint_row.chunk_id;
EXECUTE format('ALTER TABLE %I.%I DROP CONSTRAINT %I', chunk_row.schema_name, chunk_row.table_name, chunk_constraint_row.constraint_name);
END LOOP;
DROP FUNCTION _timescaledb_internal.get_partition_for_key(text);
FOR chunk_constraint_row IN
SELECT cc.*
FROM _timescaledb_catalog.chunk_constraint cc
INNER JOIN _timescaledb_catalog.dimension_slice ds ON (cc.dimension_slice_id = ds.id)
INNER JOIN _timescaledb_catalog.dimension d ON (ds.dimension_id = d.id)
WHERE d.partitioning_func IS NOT NULL
LOOP
SELECT * INTO STRICT chunk_row FROM _timescaledb_catalog.chunk c WHERE c.id = chunk_constraint_row.chunk_id;
PERFORM _timescaledb_internal.chunk_constraint_add_table_constraint(chunk_constraint_row);
END LOOP;
END$$;

View File

@ -92,8 +92,6 @@ $BODY$
DECLARE
dimension_slice_row _timescaledb_catalog.dimension_slice;
dimension_row _timescaledb_catalog.dimension;
proargtype OID;
typecast TEXT = '';
parts TEXT[];
BEGIN
SELECT * INTO STRICT dimension_slice_row
@ -105,39 +103,26 @@ BEGIN
WHERE id = dimension_slice_row.dimension_id;
IF dimension_row.partitioning_func IS NOT NULL THEN
SELECT proargtypes[0] INTO STRICT proargtype
FROM pg_proc p, pg_namespace n
WHERE n.nspname = dimension_row.partitioning_func_schema
AND p.proname = dimension_row.partitioning_func
AND n.oid = p.pronamespace;
-- Check if we are using a legacy partitioning function
-- that only takes text input
IF proargtype = 'text'::regtype THEN
typecast := '::text';
END IF;
IF _timescaledb_internal.dimension_is_finite(dimension_slice_row.range_start) THEN
parts = parts || format(
$$
%1$I.%2$I(%3$I%4$s) >= %5$L
%1$I.%2$I(%3$I) >= %4$L
$$,
dimension_row.partitioning_func_schema,
dimension_row.partitioning_func,
dimension_row.column_name,
typecast,
dimension_slice_row.range_start);
END IF;
IF _timescaledb_internal.dimension_is_finite(dimension_slice_row.range_end) THEN
parts = parts || format(
$$
%1$I.%2$I(%3$I%4$s) < %5$L
%1$I.%2$I(%3$I) < %4$L
$$,
dimension_row.partitioning_func_schema,
dimension_row.partitioning_func,
dimension_row.column_name,
typecast,
dimension_slice_row.range_end);
END IF;

View File

@ -68,32 +68,8 @@ create_partition_func_equals_const(ParseState *pstate, PartitioningInfo *pi, Var
Node *f_var;
Node *f_const;
if (pi->partfunc.paramtype == TEXTOID)
{
/* Path for deprecated partitioning function taking text input */
if (var_expr->vartype == TEXTOID)
{
var_node = (Node *) copyObject(var_expr);
const_node = (Node *) copyObject(const_expr);
}
else
{
var_node = coerce_to_target_type(pstate, (Node *) var_expr,
var_expr->vartype,
TEXTOID, -1, COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST, -1);
const_node = coerce_to_target_type(pstate, (Node *) const_expr,
const_expr->consttype,
TEXTOID, -1, COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST, -1);
}
}
else
{
/* Path for partitioning func taking anyelement */
var_node = (Node *) copyObject(var_expr);
const_node = (Node *) copyObject(const_expr);
}
var_node = (Node *) copyObject(var_expr);
const_node = (Node *) copyObject(const_expr);
args_func_var = list_make1(var_node);
args_func_const = list_make1(const_node);

View File

@ -32,15 +32,15 @@ partitioning_func_set_func_fmgr(PartitioningFunc *pf)
FuncnameGetCandidates(partitioning_func_qualified_name(pf),
1, NULL, false, false, false);
if (funclist == NULL || funclist->next)
elog(ERROR, "Could not resolve the partitioning function");
while (funclist != NULL &&
(funclist->nargs != 1 || funclist->args[0] != ANYELEMENTOID))
funclist = funclist->next;
if (NULL == funclist)
elog(ERROR, "Partitioning function not found");
pf->paramtype = funclist->args[0];
if (!(funclist->nargs == 1 &&
(pf->paramtype == TEXTOID || pf->paramtype == ANYELEMENTOID)))
elog(ERROR, "Invalid partitioning function signature");
fmgr_info_cxt(funclist->oid, &pf->func_fmgr, CurrentMemoryContext);
}
@ -132,25 +132,12 @@ partitioning_info_create(const char *schema,
/*
* Apply the partitioning function of a hypertable to a value.
*
* We support both partitioning functions with the legacy signature int
* func(text) and the new signature int func(anyelement).
* We support both partitioning functions with the signature int
* func(anyelement).
*/
int32
partitioning_func_apply(PartitioningInfo *pinfo, Datum value)
{
if (pinfo->partfunc.paramtype == TEXTOID)
{
/* Legacy function signature. We need to convert the datum to text. */
Oid funcid = find_text_coercion_func(pinfo->typcache_entry->type_id);
if (!OidIsValid(funcid))
elog(ERROR, "Could not coerce type %u to text",
pinfo->typcache_entry->type_id);
value = OidFunctionCall1(funcid, value);
value = CStringGetTextDatum(DatumGetCString(value));
}
return DatumGetInt32(FunctionCall1(&pinfo->partfunc.func_fmgr, value));
}
@ -168,32 +155,6 @@ partitioning_func_apply_tuple(PartitioningInfo *pinfo, HeapTuple tuple, TupleDes
return partitioning_func_apply(pinfo, value);
}
/* _timescaledb_catalog.get_partition_for_key(key TEXT) RETURNS INT */
PGDLLEXPORT Datum get_partition_for_key(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(get_partition_for_key);
/*
* Deprecated function to calculate partition hash values.
*
* This function assumes text input only.
*/
Datum
get_partition_for_key(PG_FUNCTION_ARGS)
{
struct varlena *data = PG_GETARG_VARLENA_PP(0);
uint32 hash_u;
int32 res;
hash_u = DatumGetUInt32(hash_any((unsigned char *) VARDATA_ANY(data),
VARSIZE_ANY_EXHDR(data)));
res = (int32) (hash_u & 0x7fffffff); /* Only positive numbers */
PG_FREE_IF_COPY(data, 0);
PG_RETURN_INT32(res);
}
/*
* Resolve the type of the argument passed to a function.
*
@ -235,6 +196,52 @@ resolve_function_argtype(FunctionCallInfo fcinfo)
return argtype;
}
/* _timescaledb_catalog.get_partition_for_key(key anyelement) RETURNS INT */
PGDLLEXPORT Datum get_partition_for_key(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(get_partition_for_key);
/*
* Partition hash function that first converts all inputs to text before
* hashing.
*/
Datum
get_partition_for_key(PG_FUNCTION_ARGS)
{
Datum arg = PG_GETARG_DATUM(0);
struct varlena *data;
Oid argtype;
uint32 hash_u;
int32 res;
if (PG_NARGS() != 1)
elog(ERROR, "Unexpected number of arguments to partitioning function");
argtype = resolve_function_argtype(fcinfo);
if (argtype != TEXTOID)
{
/* Not TEXT input -> need to convert to text */
Oid funcid = find_text_coercion_func(argtype);
if (!OidIsValid(funcid))
elog(ERROR, "Could not coerce type %u to text",
argtype);
arg = OidFunctionCall1(funcid, arg);
arg = CStringGetTextDatum(DatumGetCString(arg));
}
data = DatumGetTextPP(arg);
hash_u = DatumGetUInt32(hash_any((unsigned char *) VARDATA_ANY(data),
VARSIZE_ANY_EXHDR(data)));
res = (int32) (hash_u & 0x7fffffff); /* Only positive numbers */
PG_FREE_IF_COPY(data, 0);
PG_RETURN_INT32(res);
}
PGDLLEXPORT Datum get_partition_hash(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(get_partition_hash);

View File

@ -113,11 +113,11 @@ typedef struct ProcessUtilityArgs
ParamListInfo params;
DestReceiver *dest;
char *completion_tag;
} ProcessUtilityArgs;
} ProcessUtilityArgs;
/* Call the default ProcessUtility and handle PostgreSQL version differences */
static void
prev_ProcessUtility(ProcessUtilityArgs * args)
prev_ProcessUtility(ProcessUtilityArgs *args)
{
if (prev_ProcessUtility_hook != NULL)
{

View File

@ -40,13 +40,13 @@ WHERE h.schema_name = 'public' AND (h.table_name = 'drop_chunk_test1' OR h.table
--------+------+------+-------
(0 rows)
SELECT _timescaledb_internal.get_partition_for_key('dev1');
SELECT _timescaledb_internal.get_partition_for_key('dev1'::text);
get_partition_for_key
-----------------------
1129986420
(1 row)
SELECT _timescaledb_internal.get_partition_for_key('dev7');
SELECT _timescaledb_internal.get_partition_for_key('dev7'::varchar(5));
get_partition_for_key
-----------------------
449729092

View File

@ -250,3 +250,52 @@ SELECT _timescaledb_internal.get_partition_hash(id::text) FROM hash_test;
1516350201
(1 row)
-- Test legacy function that converts values to text first
SELECT _timescaledb_internal.get_partition_for_key('4b6a5eec-b344-11e7-abc4-cec278b6b50a'::text);
get_partition_for_key
-----------------------
934882099
(1 row)
SELECT _timescaledb_internal.get_partition_for_key('4b6a5eec-b344-11e7-abc4-cec278b6b50a'::varchar);
get_partition_for_key
-----------------------
934882099
(1 row)
SELECT _timescaledb_internal.get_partition_for_key(187);
get_partition_for_key
-----------------------
1161071810
(1 row)
SELECT _timescaledb_internal.get_partition_for_key(187::bigint);
get_partition_for_key
-----------------------
1161071810
(1 row)
SELECT _timescaledb_internal.get_partition_for_key(187::numeric);
get_partition_for_key
-----------------------
1161071810
(1 row)
SELECT _timescaledb_internal.get_partition_for_key(187::double precision);
get_partition_for_key
-----------------------
1161071810
(1 row)
SELECT _timescaledb_internal.get_partition_for_key(int4range(10, 20));
get_partition_for_key
-----------------------
505239042
(1 row)
SELECT _timescaledb_internal.get_partition_hash('08002b:010203'::macaddr);
get_partition_hash
--------------------
294987870
(1 row)

View File

@ -22,23 +22,23 @@ SELECT * FROM test.show_constraintsp('_timescaledb_internal._hyper_1_%_chunk');
Table | Constraint | Type | Columns | Index | Expr
----------------------------------------+--------------+------+----------+-------+------------------------------------------------------------------------------------------------------------------------------------------------
_timescaledb_internal._hyper_1_1_chunk | constraint_1 | c | {time} | - | (("time" >= 'Wed Feb 22 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Fri Mar 24 17:00:00 2017 PDT'::timestamp with time zone))
_timescaledb_internal._hyper_1_1_chunk | constraint_2 | c | {device} | - | (_timescaledb_internal.get_partition_for_key((device)::text) >= 1073741823)
_timescaledb_internal._hyper_1_1_chunk | constraint_2 | c | {device} | - | (_timescaledb_internal.get_partition_for_key(device) >= 1073741823)
_timescaledb_internal._hyper_1_2_chunk | constraint_1 | c | {time} | - | (("time" >= 'Wed Feb 22 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Fri Mar 24 17:00:00 2017 PDT'::timestamp with time zone))
_timescaledb_internal._hyper_1_2_chunk | constraint_3 | c | {device} | - | (_timescaledb_internal.get_partition_for_key((device)::text) < 1073741823)
_timescaledb_internal._hyper_1_2_chunk | constraint_3 | c | {device} | - | (_timescaledb_internal.get_partition_for_key(device) < 1073741823)
(4 rows)
-- Make sure constraint exclusion works on device column
EXPLAIN (verbose, costs off)
SELECT * FROM part_legacy WHERE device = 1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Append
-> Seq Scan on public.part_legacy
Output: part_legacy."time", part_legacy.temp, part_legacy.device
Filter: ((part_legacy.device = 1) AND (_timescaledb_internal.get_partition_for_key((part_legacy.device)::text) = 1516350201))
Filter: ((part_legacy.device = 1) AND (_timescaledb_internal.get_partition_for_key(part_legacy.device) = 1516350201))
-> Seq Scan on _timescaledb_internal._hyper_1_1_chunk
Output: _hyper_1_1_chunk."time", _hyper_1_1_chunk.temp, _hyper_1_1_chunk.device
Filter: ((_hyper_1_1_chunk.device = 1) AND (_timescaledb_internal.get_partition_for_key((_hyper_1_1_chunk.device)::text) = 1516350201))
Filter: ((_hyper_1_1_chunk.device = 1) AND (_timescaledb_internal.get_partition_for_key(_hyper_1_1_chunk.device) = 1516350201))
(7 rows)
CREATE TABLE part_new(time timestamptz, temp float, device int);

View File

@ -16,8 +16,8 @@ WHERE h.schema_name = 'public' AND (h.table_name = 'drop_chunk_test1' OR h.table
\dt "_timescaledb_internal".*
SELECT _timescaledb_internal.get_partition_for_key('dev1');
SELECT _timescaledb_internal.get_partition_for_key('dev7');
SELECT _timescaledb_internal.get_partition_for_key('dev1'::text);
SELECT _timescaledb_internal.get_partition_for_key('dev7'::varchar(5));
INSERT INTO PUBLIC.drop_chunk_test1 VALUES(1, 1.0, 'dev1');
INSERT INTO PUBLIC.drop_chunk_test1 VALUES(2, 2.0, 'dev1');

View File

@ -62,3 +62,13 @@ SELECT _timescaledb_internal.get_partition_hash(id) FROM hash_test;
SELECT _timescaledb_internal.get_partition_hash(value) FROM hash_test;
-- Test coerced value
SELECT _timescaledb_internal.get_partition_hash(id::text) FROM hash_test;
-- Test legacy function that converts values to text first
SELECT _timescaledb_internal.get_partition_for_key('4b6a5eec-b344-11e7-abc4-cec278b6b50a'::text);
SELECT _timescaledb_internal.get_partition_for_key('4b6a5eec-b344-11e7-abc4-cec278b6b50a'::varchar);
SELECT _timescaledb_internal.get_partition_for_key(187);
SELECT _timescaledb_internal.get_partition_for_key(187::bigint);
SELECT _timescaledb_internal.get_partition_for_key(187::numeric);
SELECT _timescaledb_internal.get_partition_for_key(187::double precision);
SELECT _timescaledb_internal.get_partition_for_key(int4range(10, 20));
SELECT _timescaledb_internal.get_partition_hash('08002b:010203'::macaddr);

View File

@ -3,7 +3,7 @@
DO $$
BEGIN
ASSERT( _timescaledb_internal.get_partition_for_key('') = 669664877 );
ASSERT( _timescaledb_internal.get_partition_for_key('dev1') = 1129986420 );
ASSERT( _timescaledb_internal.get_partition_for_key('longlonglonglongpartitionkey') = 1169179734);
ASSERT( _timescaledb_internal.get_partition_for_key(''::text) = 669664877 );
ASSERT( _timescaledb_internal.get_partition_for_key('dev1'::text) = 1129986420 );
ASSERT( _timescaledb_internal.get_partition_for_key('longlonglonglongpartitionkey'::text) = 1169179734);
END$$;