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