forked from pjungwir/aggs_for_vecs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vec_to_count.c
117 lines (101 loc) · 3.32 KB
/
vec_to_count.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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
Datum vec_to_count_transfn(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(vec_to_count_transfn);
/**
* Returns an of n elements,
* which each element is the count of non-NULLs found in that position
* from all input arrays.
*
* by Paul A. Jungwirth
*/
Datum
vec_to_count_transfn(PG_FUNCTION_ARGS)
{
Oid elemTypeId;
int16 elemTypeWidth;
bool elemTypeByValue;
char elemTypeAlignmentCode;
int currentLength;
MemoryContext aggContext;
VecArrayBuildState *state = NULL;
ArrayType *currentArray;
int arrayLength;
Datum *currentVals;
bool *currentNulls;
int i;
if (!AggCheckCallContext(fcinfo, &aggContext)) {
elog(ERROR, "vec_to_count_transfn called in non-aggregate context");
}
// PG_ARGISNULL tests for SQL NULL,
// but after the first pass we can have a
// value that is non-SQL-NULL but still is C NULL.
if (!PG_ARGISNULL(0)) {
state = (VecArrayBuildState *)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 (state == NULL) {
// Since we have our first not-null argument
// we can initialize the state to match its length.
elemTypeId = ARR_ELEMTYPE(currentArray);
if (elemTypeId != INT2OID &&
elemTypeId != INT4OID &&
elemTypeId != INT8OID &&
elemTypeId != FLOAT4OID &&
elemTypeId != FLOAT8OID) {
ereport(ERROR, (errmsg("vec_to_count input must be array of SMALLINT, INTEGER, BIGINT, REAL, or DOUBLE PRECISION")));
}
if (ARR_NDIM(currentArray) != 1) {
ereport(ERROR, (errmsg("One-dimensional arrays are required")));
}
arrayLength = (ARR_DIMS(currentArray))[0];
// Start with all zeros:
state = initVecArrayResultWithNulls(elemTypeId, INT8OID, aggContext, arrayLength);
for (i = 0; i < arrayLength; i++) {
state->vecvalues[i].i64 = 0;
state->state.dnulls[i] = false;
}
} else {
elemTypeId = state->inputElementType;
arrayLength = state->state.nelems;
}
get_typlenbyvalalign(elemTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode);
deconstruct_array(currentArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode,
¤tVals, ¤tNulls, ¤tLength);
if (currentLength != arrayLength) {
ereport(ERROR, (errmsg("All arrays must be the same length, but we got %d vs %d", currentLength, arrayLength)));
}
for (i = 0; i < arrayLength; i++) {
if (currentNulls[i]) {
// do nothing: nulls can't change the result.
} else {
state->vecvalues[i].i64 += 1;
}
}
PG_RETURN_POINTER(state);
}
Datum vec_to_count_finalfn(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(vec_to_count_finalfn);
Datum
vec_to_count_finalfn(PG_FUNCTION_ARGS)
{
Datum result;
VecArrayBuildState *state;
int dims[1];
int lbs[1];
int i;
Assert(AggCheckCallContext(fcinfo, NULL));
state = PG_ARGISNULL(0) ? NULL : (VecArrayBuildState *)PG_GETARG_POINTER(0);
if (state == NULL)
PG_RETURN_NULL();
// Convert from our pgnums to Datums:
for (i = 0; i < state->state.nelems; i++) {
state->state.dvalues[i] = Int64GetDatum(state->vecvalues[i].i64);
}
dims[0] = state->state.nelems;
lbs[0] = 1;
result = makeMdArrayResult(&state->state, 1, dims, lbs, CurrentMemoryContext, false);
PG_RETURN_DATUM(result);
}