1212#include <utils/jsonb.h>
1313#include <utils/acl.h>
1414#include <utils/rangetypes.h>
15+ #include <utils/memutils.h>
1516#include <catalog/namespace.h>
1617#include <catalog/pg_type.h>
1718#include <access/hash.h>
@@ -158,7 +159,7 @@ partitioning_info_create(const char *schema,
158159 pinfo -> typcache_entry = lookup_type_cache (columntype , TYPECACHE_HASH_FLAGS );
159160
160161 if (pinfo -> typcache_entry -> hash_proc == InvalidOid )
161- elog (ERROR , "No hash function for type %u" , columntype );
162+ elog (ERROR , "could not find hash function for type %u" , columntype );
162163
163164 partitioning_func_set_func_fmgr (& pinfo -> partfunc );
164165
@@ -181,20 +182,13 @@ partitioning_info_create(const char *schema,
181182
182183 fmgr_info_set_expr ((Node * ) expr , & pinfo -> partfunc .func_fmgr );
183184
184- /*
185- * Set the type cache entry in fn_extra to avoid an extry lookup in the
186- * partition hash function
187- */
188- pinfo -> partfunc .func_fmgr .fn_extra = pinfo -> typcache_entry ;
189-
190185 return pinfo ;
191186}
192187
193188/*
194189 * Apply the partitioning function of a hypertable to a value.
195190 *
196- * We support both partitioning functions with the signature int
197- * func(anyelement).
191+ * We support partitioning functions with the signature (anyelement) -> int.
198192 */
199193int32
200194partitioning_func_apply (PartitioningInfo * pinfo , Datum value )
@@ -232,10 +226,10 @@ resolve_function_argtype(FunctionCallInfo fcinfo)
232226 fe = (FuncExpr * ) fcinfo -> flinfo -> fn_expr ;
233227
234228 if (NULL == fe || !IsA (fe , FuncExpr ))
235- elog (ERROR , "No function expression set when invoking partitioning function" );
229+ elog (ERROR , "no function expression set when invoking partitioning function" );
236230
237231 if (list_length (fe -> args ) != 1 )
238- elog (ERROR , "Unexpected number of arguments in function expression" );
232+ elog (ERROR , "unexpected number of arguments in function expression" );
239233
240234 node = linitial (fe -> args );
241235
@@ -250,13 +244,50 @@ resolve_function_argtype(FunctionCallInfo fcinfo)
250244 case T_CoerceViaIO :
251245 argtype = ((CoerceViaIO * ) node )-> resulttype ;
252246 break ;
247+ case T_FuncExpr :
248+ /* Argument is function, so our input is its result type */
249+ argtype = ((FuncExpr * ) node )-> funcresulttype ;
250+ break ;
253251 default :
254- elog (ERROR , "Unsupported expression argument node type %u" , nodeTag (node ));
252+ elog (ERROR , "unsupported expression argument node type %u" , nodeTag (node ));
255253 }
256254
257255 return argtype ;
258256}
259257
258+ /*
259+ * Partitioning function cache.
260+ *
261+ * Holds type information to avoid repeated lookups. The cache is allocated on a
262+ * child memory context of the context that created the associated FmgrInfo
263+ * struct. For partitioning functions invoked on the insert path, this is
264+ * typically the Hypertable cache's memory context. Hence, the type cache lives
265+ * for the duration of the hypertable cache and can be reused across multiple
266+ * invokations of the partitioning function, even across transactions.
267+ *
268+ * If the partitioning function is invoked outside the insert path, the FmgrInfo
269+ * and its memory context has a lifetime corresponding to that invokation.
270+ */
271+ typedef struct PartFuncCache
272+ {
273+ Oid argtype ;
274+ Oid coerce_funcid ;
275+ TypeCacheEntry * tce ;
276+ } PartFuncCache ;
277+
278+ static PartFuncCache *
279+ part_func_cache_create (Oid argtype , TypeCacheEntry * tce , Oid coerce_funcid , MemoryContext mcxt )
280+ {
281+ PartFuncCache * pfc ;
282+
283+ pfc = MemoryContextAlloc (mcxt , sizeof (PartFuncCache ));
284+ pfc -> argtype = argtype ;
285+ pfc -> tce = tce ;
286+ pfc -> coerce_funcid = coerce_funcid ;
287+
288+ return pfc ;
289+ }
290+
260291/* _timescaledb_catalog.get_partition_for_key(key anyelement) RETURNS INT */
261292PGDLLEXPORT Datum get_partition_for_key (PG_FUNCTION_ARGS );
262293
@@ -270,26 +301,35 @@ Datum
270301get_partition_for_key (PG_FUNCTION_ARGS )
271302{
272303 Datum arg = PG_GETARG_DATUM (0 );
304+ PartFuncCache * pfc = fcinfo -> flinfo -> fn_extra ;
273305 struct varlena * data ;
274- Oid argtype ;
275306 uint32 hash_u ;
276307 int32 res ;
277308
278309 if (PG_NARGS () != 1 )
279- elog (ERROR , "Unexpected number of arguments to partitioning function" );
280-
281- argtype = resolve_function_argtype (fcinfo );
310+ elog (ERROR , "unexpected number of arguments to partitioning function" );
282311
283- if (argtype != TEXTOID )
312+ if (NULL == pfc )
284313 {
285- /* Not TEXT input -> need to convert to text */
286- Oid funcid = find_text_coercion_func ( argtype );
314+ Oid funcid = InvalidOid ;
315+ Oid argtype = resolve_function_argtype ( fcinfo );
287316
288- if (!OidIsValid (funcid ))
289- elog (ERROR , "Could not coerce type %u to text" ,
290- argtype );
317+ if (argtype != TEXTOID )
318+ {
319+ /* Not TEXT input -> need to convert to text */
320+ funcid = find_text_coercion_func (argtype );
321+
322+ if (!OidIsValid (funcid ))
323+ elog (ERROR , "could not coerce type %u to text" , argtype );
324+ }
291325
292- arg = OidFunctionCall1 (funcid , arg );
326+ pfc = part_func_cache_create (argtype , NULL , funcid , fcinfo -> flinfo -> fn_mcxt );
327+ fcinfo -> flinfo -> fn_extra = pfc ;
328+ }
329+
330+ if (pfc -> argtype != TEXTOID )
331+ {
332+ arg = OidFunctionCall1 (pfc -> coerce_funcid , arg );
293333 arg = CStringGetTextDatum (DatumGetCString (arg ));
294334 }
295335
@@ -320,23 +360,26 @@ Datum
320360get_partition_hash (PG_FUNCTION_ARGS )
321361{
322362 Datum arg = PG_GETARG_DATUM (0 );
323- TypeCacheEntry * tce = fcinfo -> flinfo -> fn_extra ;
324- Oid argtype ;
363+ PartFuncCache * pfc = fcinfo -> flinfo -> fn_extra ;
325364 Datum hash ;
326365 int32 res ;
327366
328367 if (PG_NARGS () != 1 )
329- elog (ERROR , "Unexpected number of arguments to partitioning function" );
368+ elog (ERROR , "unexpected number of arguments to partitioning function" );
330369
331- argtype = resolve_function_argtype (fcinfo );
370+ if (NULL == pfc )
371+ {
372+ Oid argtype = resolve_function_argtype (fcinfo );
373+ TypeCacheEntry * tce = lookup_type_cache (argtype , TYPECACHE_HASH_FLAGS );
332374
333- if (tce == NULL )
334- tce = lookup_type_cache (argtype , TYPECACHE_HASH_FLAGS );
375+ pfc = part_func_cache_create (argtype , tce , InvalidOid , fcinfo -> flinfo -> fn_mcxt );
376+ fcinfo -> flinfo -> fn_extra = pfc ;
377+ }
335378
336- if (tce -> hash_proc == InvalidOid )
337- elog (ERROR , "No hash function for type %u" , argtype );
379+ if (pfc -> tce -> hash_proc == InvalidOid )
380+ elog (ERROR , "could not find hash function for type %u" , pfc -> argtype );
338381
339- hash = FunctionCall1 (& tce -> hash_proc_finfo , arg );
382+ hash = FunctionCall1 (& pfc -> tce -> hash_proc_finfo , arg );
340383
341384 /* Only positive numbers */
342385 res = (int32 ) (DatumGetUInt32 (hash ) & 0x7fffffff );
0 commit comments