-
Notifications
You must be signed in to change notification settings - Fork 5
/
vec_to_last.c
101 lines (86 loc) · 2.85 KB
/
vec_to_last.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
Datum vec_to_last_transfn(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(vec_to_last_transfn);
/**
* Returns an of n elements, which each element is the last non-null value found in that position
* from all input arrays.
*/
Datum
vec_to_last_transfn(PG_FUNCTION_ARGS)
{
Oid elemTypeId;
int16 elemTypeWidth;
bool elemTypeByValue;
char elemTypeAlignmentCode;
int currentLength;
MemoryContext aggContext;
ArrayBuildState *state = NULL;
ArrayType *currentArray;
int arrayLength;
Datum *currentVals;
bool *currentNulls;
int i;
MemoryContext old = NULL;
if (!AggCheckCallContext(fcinfo, &aggContext)) {
elog(ERROR, "vec_to_last_transfn called in non-aggregate context");
}
if (!PG_ARGISNULL(0)) {
state = (ArrayBuildState *)PG_GETARG_POINTER(0);
}
if (PG_ARGISNULL(1)) {
// just return the current state unchanged (possibly still NULL)
PG_RETURN_POINTER(state);
}
currentArray = PG_GETARG_ARRAYTYPE_P(1);
if (ARR_NDIM(currentArray) == 0) {
PG_RETURN_POINTER(state);
}
if (state == NULL) {
elemTypeId = ARR_ELEMTYPE(currentArray);
if (ARR_NDIM(currentArray) != 1) {
ereport(ERROR, (errmsg("vec_to_last: one-dimensional arrays are required, but got %d", ARR_NDIM(currentArray))));
}
arrayLength = (ARR_DIMS(currentArray))[0];
state = initArrayResultWithNulls(elemTypeId, aggContext, arrayLength);
} else {
elemTypeId = state->element_type;
arrayLength = state->nelems;
}
get_typlenbyvalalign(elemTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode);
deconstruct_array(currentArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode,
¤tVals, ¤tNulls, ¤tLength);
if (currentLength != arrayLength) {
ereport(ERROR, (errmsg("vec_to_last: all arrays must be the same length, but we got %d vs %d", currentLength, arrayLength)));
}
if (!elemTypeByValue) old = MemoryContextSwitchTo(aggContext);
for (i = 0; i < arrayLength; i++) {
if (!currentNulls[i]) {
if (state->dnulls[i]) {
state->dnulls[i] = false;
} else if (!elemTypeByValue) {
// free previously seen datum
pfree(DatumGetPointer(state->dvalues[i]));
}
state->dvalues[i] = datumCopy(currentVals[i], elemTypeByValue, elemTypeWidth);
}
}
if (!elemTypeByValue) MemoryContextSwitchTo(old);
PG_RETURN_POINTER(state);
}
Datum vec_to_last_finalfn(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(vec_to_last_finalfn);
Datum
vec_to_last_finalfn(PG_FUNCTION_ARGS)
{
Datum result;
ArrayBuildState *state;
int dims[1];
int lbs[1];
Assert(AggCheckCallContext(fcinfo, NULL));
state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *)PG_GETARG_POINTER(0);
if (state == NULL)
PG_RETURN_NULL();
dims[0] = state->nelems;
lbs[0] = 1;
result = makeMdArrayResult(state, 1, dims, lbs, CurrentMemoryContext, false);
PG_RETURN_DATUM(result);
}