1 #include "postgres.h"
2 
3 #include <math.h>
4 
5 #include "funcapi.h"
6 #include "builtins.h"
7 
8 #include "lib/stringinfo.h"
9 #include "utils/builtins.h"
10 
11 #include "orafce.h"
12 
13 PG_FUNCTION_INFO_V1(orafce_listagg1_transfn);
14 PG_FUNCTION_INFO_V1(orafce_wm_concat_transfn);
15 PG_FUNCTION_INFO_V1(orafce_listagg2_transfn);
16 PG_FUNCTION_INFO_V1(orafce_listagg_finalfn);
17 
18 PG_FUNCTION_INFO_V1(orafce_median4_transfn);
19 PG_FUNCTION_INFO_V1(orafce_median4_finalfn);
20 PG_FUNCTION_INFO_V1(orafce_median8_transfn);
21 PG_FUNCTION_INFO_V1(orafce_median8_finalfn);
22 
23 typedef struct
24 {
25 	int	alen;		/* allocated length */
26 	int	nextlen;	/* next allocated length */
27 	int	nelems;		/* number of valid entries */
28 	union
29 	{
30 		float4	*float4_values;
31 		float8  *float8_values;
32 	} d;
33 } MedianState;
34 
35 int orafce_float4_cmp(const void *a, const void *b);
36 int orafce_float8_cmp(const void *a, const void *b);
37 
38 /****************************************************************
39  * listagg
40  *
41  * Concates values and returns string.
42  *
43  * Syntax:
44  *     FUNCTION listagg(string varchar, delimiter varchar = '')
45  *      RETURNS varchar;
46  *
47  * Note: any NULL value is ignored.
48  *
49  ****************************************************************/
50 /* subroutine to initialize state */
51 static StringInfo
makeStringAggState(FunctionCallInfo fcinfo)52 makeStringAggState(FunctionCallInfo fcinfo)
53 {
54 	StringInfo	state;
55 	MemoryContext aggcontext;
56 	MemoryContext oldcontext;
57 
58 	if (!AggCheckCallContext(fcinfo, &aggcontext))
59 	{
60 		/* cannot be called directly because of internal-type argument */
61 		elog(ERROR, "listagg_transfn called in non-aggregate context");
62 	}
63 
64 	/*
65 	 * Create state in aggregate context.  It'll stay there across subsequent
66 	 * calls.
67 	 */
68 	oldcontext = MemoryContextSwitchTo(aggcontext);
69 	state = makeStringInfo();
70 	MemoryContextSwitchTo(oldcontext);
71 
72 	return state;
73 }
74 
75 static void
appendStringInfoText(StringInfo str,const text * t)76 appendStringInfoText(StringInfo str, const text *t)
77 {
78 	appendBinaryStringInfo(str, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
79 }
80 
81 Datum
orafce_listagg1_transfn(PG_FUNCTION_ARGS)82 orafce_listagg1_transfn(PG_FUNCTION_ARGS)
83 {
84 	StringInfo	state;
85 
86 	state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
87 
88 	/* Append the element unless null. */
89 	if (!PG_ARGISNULL(1))
90 	{
91 		if (state == NULL)
92 			state = makeStringAggState(fcinfo);
93 		appendStringInfoText(state, PG_GETARG_TEXT_PP(1));		/* value */
94 	}
95 
96 	/*
97 	 * The transition type for string_agg() is declared to be "internal",
98 	 * which is a pass-by-value type the same size as a pointer.
99 	 */
100 	PG_RETURN_POINTER(state);
101 }
102 
103 Datum
orafce_wm_concat_transfn(PG_FUNCTION_ARGS)104 orafce_wm_concat_transfn(PG_FUNCTION_ARGS)
105 {
106 	StringInfo	state;
107 
108 	state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
109 
110 	/* Append the element unless null. */
111 	if (!PG_ARGISNULL(1))
112 	{
113 		if (state == NULL)
114 			state = makeStringAggState(fcinfo);
115 		else
116 			appendStringInfoChar(state, ',');
117 
118 		appendStringInfoText(state, PG_GETARG_TEXT_PP(1));		/* value */
119 	}
120 
121 	/*
122 	 * The transition type for string_agg() is declared to be "internal",
123 	 * which is a pass-by-value type the same size as a pointer.
124 	 */
125 	PG_RETURN_POINTER(state);
126 }
127 
128 
129 Datum
orafce_listagg2_transfn(PG_FUNCTION_ARGS)130 orafce_listagg2_transfn(PG_FUNCTION_ARGS)
131 {
132 	return string_agg_transfn(fcinfo);
133 }
134 
135 Datum
orafce_listagg_finalfn(PG_FUNCTION_ARGS)136 orafce_listagg_finalfn(PG_FUNCTION_ARGS)
137 {
138 	return string_agg_finalfn(fcinfo);
139 }
140 
141 static MedianState *
accumFloat4(MedianState * mstate,float4 value,MemoryContext aggcontext)142 accumFloat4(MedianState *mstate, float4 value, MemoryContext aggcontext)
143 {
144 	MemoryContext oldcontext;
145 
146 	if (mstate == NULL)
147 	{
148 		/* First call - initialize */
149 		oldcontext = MemoryContextSwitchTo(aggcontext);
150 		mstate = palloc(sizeof(MedianState));
151 		mstate->alen = 1024;
152 		mstate->nextlen = 2 * 1024;
153 		mstate->nelems = 0;
154 		mstate->d.float4_values = palloc(mstate->alen * sizeof(float4));
155 		MemoryContextSwitchTo(oldcontext);
156 	}
157 	else
158 	{
159 		/* enlarge float4_values if needed */
160 		if (mstate->nelems >= mstate->alen)
161 		{
162 			int	newlen = mstate->nextlen;
163 
164 			oldcontext = MemoryContextSwitchTo(aggcontext);
165 			mstate->nextlen += mstate->alen;
166 			mstate->alen = newlen;
167 			mstate->d.float4_values = repalloc(mstate->d.float4_values,
168 									    mstate->alen * sizeof(float4));
169 			MemoryContextSwitchTo(oldcontext);
170 		}
171 	}
172 
173 	mstate->d.float4_values[mstate->nelems++] = value;
174 
175 	return mstate;
176 }
177 
178 static MedianState *
accumFloat8(MedianState * mstate,float8 value,MemoryContext aggcontext)179 accumFloat8(MedianState *mstate, float8 value, MemoryContext aggcontext)
180 {
181 	MemoryContext oldcontext;
182 
183 	if (mstate == NULL)
184 	{
185 		/* First call - initialize */
186 		oldcontext = MemoryContextSwitchTo(aggcontext);
187 		mstate = palloc(sizeof(MedianState));
188 		mstate->alen = 1024;
189 		mstate->nextlen = 2 * 1024;
190 		mstate->nelems = 0;
191 		mstate->d.float8_values = palloc(mstate->alen * sizeof(float8));
192 		MemoryContextSwitchTo(oldcontext);
193 	}
194 	else
195 	{
196 		/* enlarge float4_values if needed */
197 		if (mstate->nelems >= mstate->alen)
198 		{
199 			int	newlen = mstate->nextlen;
200 
201 			oldcontext = MemoryContextSwitchTo(aggcontext);
202 			mstate->nextlen += mstate->alen;
203 			mstate->alen = newlen;
204 			mstate->d.float8_values = repalloc(mstate->d.float8_values,
205 									    mstate->alen * sizeof(float8));
206 			MemoryContextSwitchTo(oldcontext);
207 		}
208 	}
209 
210 	mstate->d.float8_values[mstate->nelems++] = value;
211 
212 	return mstate;
213 }
214 
215 Datum
orafce_median4_transfn(PG_FUNCTION_ARGS)216 orafce_median4_transfn(PG_FUNCTION_ARGS)
217 {
218 	MemoryContext	aggcontext;
219 	MedianState *state = NULL;
220 	float4 elem;
221 
222 	if (!AggCheckCallContext(fcinfo, &aggcontext))
223 	{
224 		/* cannot be called directly because of internal-type argument */
225 		elog(ERROR, "median4_transfn called in non-aggregate context");
226 	}
227 
228 	state = PG_ARGISNULL(0) ? NULL : (MedianState *) PG_GETARG_POINTER(0);
229 	if (PG_ARGISNULL(1))
230 		PG_RETURN_POINTER(state);
231 
232 	elem = PG_GETARG_FLOAT4(1);
233 	state = accumFloat4(state, elem, aggcontext);
234 
235 	PG_RETURN_POINTER(state);
236 }
237 
238 int
orafce_float4_cmp(const void * _a,const void * _b)239 orafce_float4_cmp(const void *_a, const void *_b)
240 {
241 	float4 a = *((float4 *) _a);
242 	float4 b = *((float4 *) _b);
243 
244 	if (isnan(a))
245 	{
246 		if (isnan(b))
247 			return 0;
248 		else
249 			return 1;
250 	}
251 	else if (isnan(b))
252 	{
253 		return -1;
254 	}
255 	else
256 	{
257 		if (a > b)
258 			return 1;
259 		else if (a < b)
260 			return -1;
261 		else
262 			return 0;
263 	}
264 }
265 
266 Datum
orafce_median4_finalfn(PG_FUNCTION_ARGS)267 orafce_median4_finalfn(PG_FUNCTION_ARGS)
268 {
269 	MedianState *state = NULL;
270 	int	lidx;
271 	int	hidx;
272 	float4 result;
273 
274 	if (PG_ARGISNULL(0))
275 		PG_RETURN_NULL();
276 
277 	state = (MedianState *) PG_GETARG_POINTER(0);
278 
279 	if (state == NULL)
280 		PG_RETURN_NULL();
281 
282 	qsort(state->d.float4_values, state->nelems, sizeof(float4), orafce_float4_cmp);
283 
284 	lidx = state->nelems / 2 + 1 - 1;
285 	hidx = (state->nelems + 1) / 2 - 1;
286 
287 	if (lidx == hidx)
288 		result = state->d.float4_values[lidx];
289 	else
290 		result = (state->d.float4_values[lidx] + state->d.float4_values[hidx]) / 2.0f;
291 
292 	PG_RETURN_FLOAT4(result);
293 }
294 
295 Datum
orafce_median8_transfn(PG_FUNCTION_ARGS)296 orafce_median8_transfn(PG_FUNCTION_ARGS)
297 {
298 	MemoryContext	aggcontext;
299 	MedianState *state = NULL;
300 	float8 elem;
301 
302 	if (!AggCheckCallContext(fcinfo, &aggcontext))
303 	{
304 		/* cannot be called directly because of internal-type argument */
305 		elog(ERROR, "median4_transfn called in non-aggregate context");
306 	}
307 
308 	state = PG_ARGISNULL(0) ? NULL : (MedianState *) PG_GETARG_POINTER(0);
309 	if (PG_ARGISNULL(1))
310 		PG_RETURN_POINTER(state);
311 
312 	elem = PG_GETARG_FLOAT8(1);
313 	state = accumFloat8(state, elem, aggcontext);
314 
315 	PG_RETURN_POINTER(state);
316 }
317 
318 int
orafce_float8_cmp(const void * _a,const void * _b)319 orafce_float8_cmp(const void *_a, const void *_b)
320 {
321 	float8 a = *((float8 *) _a);
322 	float8 b = *((float8 *) _b);
323 
324 	if (isnan(a))
325 	{
326 		if (isnan(b))
327 			return 0;
328 		else
329 			return 1;
330 	}
331 	else if (isnan(b))
332 	{
333 		return -1;
334 	}
335 	else
336 	{
337 		if (a > b)
338 			return 1;
339 		else if (a < b)
340 			return -1;
341 		else
342 			return 0;
343 	}
344 }
345 
346 
347 Datum
orafce_median8_finalfn(PG_FUNCTION_ARGS)348 orafce_median8_finalfn(PG_FUNCTION_ARGS)
349 {
350 	MedianState *state = NULL;
351 	int	lidx;
352 	int	hidx;
353 	float8 result;
354 
355 	if (PG_ARGISNULL(0))
356 		PG_RETURN_NULL();
357 
358 	state = (MedianState *) PG_GETARG_POINTER(0);
359 
360 	if (state == NULL)
361 		PG_RETURN_NULL();
362 
363 	qsort(state->d.float8_values, state->nelems, sizeof(float8), orafce_float8_cmp);
364 
365 	lidx = state->nelems / 2 + 1 - 1;
366 	hidx = (state->nelems + 1) / 2 - 1;
367 
368 	if (lidx == hidx)
369 		result = state->d.float8_values[lidx];
370 	else
371 		result = (state->d.float8_values[lidx] + state->d.float8_values[hidx]) / 2.0;
372 
373 	PG_RETURN_FLOAT8(result);
374 }
375