6565#include "catalog/ag_label.h"
6666#include "utils/ag_func.h"
6767#include "utils/agtype_traversal.h"
68+ #include "common/hashfn.h"
69+
70+
6871/* State structure for Percentile aggregate functions */
6972typedef struct PercentileGroupAggState
7073{
@@ -98,6 +101,64 @@ typedef enum /* type categories for datum_to_agtype */
98101 AGT_TYPE_OTHER /* all else */
99102} agt_type_category ;
100103
104+ #include "lib/simplehash.h"
105+
106+ /*
107+ * SQL function argument entry.
108+ */
109+ typedef struct ArgType
110+ {
111+ Oid type_oid ;
112+ Datum value ;
113+ bool is_null ;
114+ } ArgType ;
115+
116+ /*
117+ * Used for caching type information.
118+ */
119+ typedef struct TypeInfo
120+ {
121+ Oid oid ;
122+ agt_type_category category ;
123+ Oid outfuncoid ;
124+ uint32 hash_value ;
125+ char status ;
126+ } TypeInfo ;
127+
128+ #define SH_PREFIX type_cache
129+ #define SH_ELEMENT_TYPE TypeInfo
130+ #define SH_KEY_TYPE Oid
131+ #define SH_KEY oid
132+ #define SH_EQUAL (tb , a , b ) (a == b)
133+ #define SH_HASH_KEY (tb , key ) hash_uint32((uint32) key)
134+ #define SH_STORE_HASH
135+ #define SH_GET_HASH (tb , a ) (a)->hash_value
136+ #define SH_SCOPE static __attribute__((unused))
137+
138+ #define SH_DECLARE
139+ #define SH_DEFINE
140+ #include "lib/simplehash.h"
141+
142+ /*
143+ * Initial size of type cache hash table.
144+ */
145+ #define INIT_TYPE_CACHE_SIZE 8
146+
147+ /*
148+ * Cache for the agtype_build_map function.
149+ * Stores type information cache such as type category and outfuncoid.
150+ * Also stores a temporary memory context which is used for agtype_value allocations.
151+ */
152+ typedef struct
153+ {
154+ type_cache_hash * type_cache ;
155+
156+ MemoryContext tmp_ctx ;
157+
158+ } agtype_build_map_cache ;
159+
160+
161+
101162static inline Datum agtype_from_cstring (char * str , int len );
102163size_t check_string_length (size_t len );
103164static void agtype_in_agtype_annotation (void * pstate , char * annotation );
@@ -2860,6 +2921,114 @@ Datum edge_to_jsonb(PG_FUNCTION_ARGS)
28602921 PG_RETURN_DATUM (result );
28612922}
28622923
2924+ /*
2925+ * Init temporary memory context and empty type cache.
2926+ */
2927+ static agtype_build_map_cache * init_agtype_type_cache (FunctionCallInfo fcinfo )
2928+ {
2929+ MemoryContext oldctx ;
2930+ agtype_build_map_cache * cache ;
2931+
2932+ oldctx = MemoryContextSwitchTo (fcinfo -> flinfo -> fn_mcxt );
2933+
2934+ cache = palloc (sizeof (agtype_build_map_cache ));
2935+
2936+ cache -> type_cache =
2937+ type_cache_create (fcinfo -> flinfo -> fn_mcxt , 8 , NULL );
2938+
2939+ cache -> tmp_ctx =
2940+ AllocSetContextCreate (fcinfo -> flinfo -> fn_mcxt ,
2941+ "agtype_build_map tmp" ,
2942+ ALLOCSET_DEFAULT_SIZES );
2943+
2944+ fcinfo -> flinfo -> fn_extra = cache ;
2945+
2946+ MemoryContextSwitchTo (oldctx );
2947+
2948+ return cache ;
2949+ }
2950+
2951+ /*
2952+ * Get TypeInfo from the cache if present.
2953+ * If not, get type information from agtype_categorize_type function and cache the result.
2954+ */
2955+ static TypeInfo * get_type_cache (FunctionCallInfo fcinfo , Oid type )
2956+ {
2957+ MemoryContext oldctx ;
2958+ agtype_build_map_cache * cache ;
2959+ TypeInfo * entry ;
2960+ bool found ;
2961+
2962+ cache = (agtype_build_map_cache * ) fcinfo -> flinfo -> fn_extra ;
2963+
2964+ entry =
2965+ type_cache_lookup (cache -> type_cache ,
2966+ type );
2967+
2968+ if (entry != NULL )
2969+ {
2970+ return entry ;
2971+ }
2972+
2973+
2974+ /*
2975+ * Type was not cached.
2976+ * Insert new entry.
2977+ */
2978+ oldctx = MemoryContextSwitchTo (fcinfo -> flinfo -> fn_mcxt );
2979+
2980+ entry =
2981+ type_cache_insert (cache -> type_cache ,
2982+ type ,
2983+ & found );
2984+
2985+ MemoryContextSwitchTo (oldctx );
2986+ Assert (!found );
2987+
2988+ entry -> oid = type ;
2989+
2990+
2991+ if (type == InvalidOid )
2992+ {
2993+ entry -> category = AGT_TYPE_NULL ;
2994+ entry -> outfuncoid = InvalidOid ;
2995+ }
2996+ else
2997+ {
2998+ agtype_categorize_type (type ,
2999+ & entry -> category ,
3000+ & entry -> outfuncoid );
3001+ }
3002+
3003+ return entry ;
3004+ }
3005+
3006+ /*
3007+ * Helper function for getting argument info. Writes the result into ArgType *
3008+ */
3009+ static inline void get_arg_info (ArgType * arg ,
3010+ FunctionCallInfo fcinfo ,
3011+ Datum * args ,
3012+ bool * nulls ,
3013+ Oid * types ,
3014+ int idx ,
3015+ bool is_variadic )
3016+ {
3017+ if (!is_variadic )
3018+ {
3019+ arg -> value = fcinfo -> args [idx ].value ;
3020+ arg -> is_null = fcinfo -> args [idx ].isnull ;
3021+ arg -> type_oid = get_fn_expr_argtype (fcinfo -> flinfo , idx );
3022+ }
3023+ else
3024+ {
3025+ arg -> value = args [idx ];
3026+ arg -> is_null = nulls [idx ];
3027+ arg -> type_oid = types [idx ];
3028+ }
3029+
3030+ }
3031+
28633032static agtype_value * agtype_build_map_as_agtype_value (FunctionCallInfo fcinfo )
28643033{
28653034 int nargs ;
@@ -2868,55 +3037,71 @@ static agtype_value *agtype_build_map_as_agtype_value(FunctionCallInfo fcinfo)
28683037 Datum * args ;
28693038 bool * nulls ;
28703039 Oid * types ;
3040+
3041+ bool is_expr_variadic = get_fn_expr_variadic (fcinfo -> flinfo );
28713042
2872- /* build argument values to build the object */
2873- nargs = extract_variadic_args (fcinfo , 0 , true, & args , & types , & nulls );
3043+ if (!is_expr_variadic )
3044+ {
3045+ nargs = fcinfo -> nargs ;
3046+ }
3047+ else
3048+ {
3049+ /* build argument values to build the object */
3050+ nargs = extract_variadic_args (fcinfo , 0 , true, & args , & types , & nulls );
3051+ }
28743052
28753053 if (nargs < 0 )
28763054 {
28773055 return NULL ;
28783056 }
3057+
28793058
28803059 if (nargs % 2 != 0 )
28813060 {
28823061 ereport (
28833062 ERROR ,
28843063 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
2885- errmsg ("argument list must have been even number of elements" ),
2886- errhint ("The arguments of agtype_build_map() must consist of alternating keys and values." )));
3064+ errmsg ("argument list must have been even number of elements" ),
3065+ errhint ("The arguments of agtype_build_map() must consist of alternating keys and values." )));
28873066 }
28883067
28893068 memset (& result , 0 , sizeof (agtype_in_state ));
28903069
28913070 result .res = push_agtype_value (& result .parse_state , WAGT_BEGIN_OBJECT ,
2892- NULL );
3071+ NULL );
28933072
28943073 /* iterate through the arguments and build the object */
28953074 for (i = 0 ; i < nargs ; i += 2 )
28963075 {
3076+ TypeInfo * type_info ;
3077+ ArgType arg_info ;
3078+
3079+ get_arg_info (& arg_info , fcinfo , args , nulls , types , i , is_expr_variadic );
3080+ type_info = get_type_cache (fcinfo , arg_info .type_oid );
3081+
28973082 /* process key */
2898- if (nulls [ i ] )
3083+ if (arg_info . is_null )
28993084 {
29003085 ereport (ERROR ,
29013086 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
2902- errmsg ("argument %d: key must not be null" , i + 1 )));
3087+ errmsg ("argument %d: key must not be null" , i + 1 )));
29033088 }
29043089
29053090 /*
2906- * If the key is agtype, we need to extract it as an agtype string and
2907- * push the value.
2908- */
2909- if (types [ i ] == AGTYPEOID )
3091+ * If the key is agtype, we need to extract it as an agtype string and
3092+ * push the value.
3093+ */
3094+ if (arg_info . type_oid == AGTYPEOID )
29103095 {
29113096 agtype_value * agtv = NULL ;
29123097
2913- agtv = tostring_helper (args [ i ], types [ i ] ,
2914- "agtype_build_map_as_agtype_value" );
3098+ agtv = tostring_helper (arg_info . value , arg_info . type_oid ,
3099+ "agtype_build_map_as_agtype_value" );
29153100 if (agtv == NULL )
29163101 {
29173102 ereport (ERROR ,
29183103 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
2919- errmsg ("argument %d: key must not be null" , i + 1 )));
3104+ errmsg ("argument %d: key must not be null" , i + 1 )));
29203105
29213106 }
29223107
@@ -2927,11 +3112,26 @@ static agtype_value *agtype_build_map_as_agtype_value(FunctionCallInfo fcinfo)
29273112 }
29283113 else
29293114 {
2930- add_agtype (args [i ], false, & result , types [i ], true);
3115+ datum_to_agtype (arg_info .value ,
3116+ false,
3117+ & result ,
3118+ type_info -> category ,
3119+ type_info -> outfuncoid ,
3120+ true);
29313121 }
2932-
2933- /* process value */
2934- add_agtype (args [i + 1 ], nulls [i + 1 ], & result , types [i + 1 ], false);
3122+ get_arg_info (& arg_info , fcinfo , args , nulls , types , i + 1 , is_expr_variadic );
3123+ type_info = get_type_cache (fcinfo , arg_info .type_oid );
3124+
3125+ datum_to_agtype (arg_info .value ,
3126+ arg_info .is_null ,
3127+ & result ,
3128+ arg_info .is_null
3129+ ? AGT_TYPE_NULL
3130+ : type_info -> category ,
3131+ arg_info .is_null
3132+ ? InvalidOid
3133+ : type_info -> outfuncoid ,
3134+ false);
29353135 }
29363136
29373137 result .res = push_agtype_value (& result .parse_state , WAGT_END_OBJECT , NULL );
@@ -2945,21 +3145,36 @@ PG_FUNCTION_INFO_V1(agtype_build_map);
29453145 */
29463146Datum agtype_build_map (PG_FUNCTION_ARGS )
29473147{
3148+ agtype_build_map_cache * cache ;
3149+ MemoryContext oldctx ;
29483150 agtype_value * result = NULL ;
29493151 agtype * agt_result = NULL ;
29503152
3153+ cache = (agtype_build_map_cache * ) fcinfo -> flinfo -> fn_extra ;
3154+
3155+ if (cache == NULL )
3156+ {
3157+ cache = init_agtype_type_cache (fcinfo );
3158+ }
3159+
3160+ MemoryContextReset (cache -> tmp_ctx );
3161+
3162+ /*
3163+ * Use temporary memory context for agtype_value allocations.
3164+ */
3165+ oldctx = MemoryContextSwitchTo (cache -> tmp_ctx );
3166+
29513167 result = agtype_build_map_as_agtype_value (fcinfo );
3168+
3169+ MemoryContextSwitchTo (oldctx );
3170+
29523171 if (result == NULL )
2953- {
29543172 PG_RETURN_NULL ();
2955- }
29563173
29573174 agt_result = agtype_value_to_agtype (result );
2958- pfree_agtype_value (result );
29593175
29603176 PG_RETURN_POINTER (agt_result );
29613177}
2962-
29633178PG_FUNCTION_INFO_V1 (agtype_build_map_noargs );
29643179
29653180/*
@@ -2989,20 +3204,34 @@ PG_FUNCTION_INFO_V1(agtype_build_map_nonull);
29893204 */
29903205Datum agtype_build_map_nonull (PG_FUNCTION_ARGS )
29913206{
3207+ agtype_build_map_cache * cache ;
3208+ MemoryContext oldctx ;
29923209 agtype_value * result = NULL ;
29933210 agtype * agt_result ;
29943211
2995- result = agtype_build_map_as_agtype_value (fcinfo );
2996- if (result == NULL )
3212+ cache = (agtype_build_map_cache * ) fcinfo -> flinfo -> fn_extra ;
3213+
3214+ if (cache == NULL )
29973215 {
2998- PG_RETURN_NULL ( );
3216+ cache = init_agtype_type_cache ( fcinfo );
29993217 }
30003218
3001- remove_null_from_agtype_object (result );
3002- agt_result = agtype_value_to_agtype (result );
3003-
3004- pfree_agtype_value (result );
3219+ MemoryContextReset (cache -> tmp_ctx );
30053220
3221+ oldctx = MemoryContextSwitchTo (cache -> tmp_ctx );
3222+
3223+ result = agtype_build_map_as_agtype_value (fcinfo );
3224+
3225+ if (result != NULL )
3226+ remove_null_from_agtype_object (result );
3227+
3228+ MemoryContextSwitchTo (oldctx );
3229+
3230+ if (result == NULL )
3231+ PG_RETURN_NULL ();
3232+
3233+ agt_result = agtype_value_to_agtype (result );
3234+
30063235 PG_RETURN_POINTER (agt_result );
30073236}
30083237
0 commit comments