Datum count_distinct_append(PG_FUNCTION_ARGS) { element_set_t *eset; /* info for anyelement */ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1); Datum element = PG_GETARG_DATUM(1); /* memory contexts */ MemoryContext oldcontext; MemoryContext aggcontext; /* * If the new value is NULL, we simply return the current aggregate state * (it might be NULL, so check it). */ if (PG_ARGISNULL(1) && PG_ARGISNULL(0)) PG_RETURN_NULL(); else if (PG_ARGISNULL(1)) PG_RETURN_DATUM(PG_GETARG_DATUM(0)); /* from now on we know the new value is not NULL */ /* switch to the per-group hash-table memory context */ GET_AGG_CONTEXT("count_distinct_append", fcinfo, aggcontext); oldcontext = MemoryContextSwitchTo(aggcontext); /* init the hash table, if needed */ if (PG_ARGISNULL(0)) { int16 typlen; bool typbyval; char typalign; /* get type information for the second parameter (anyelement item) */ get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); /* we can't handle varlena types yet or values passed by reference */ if ((typlen < 0) || (! typbyval)) elog(ERROR, "count_distinct handles only fixed-length types passed by value"); eset = init_set(typlen, typalign, aggcontext); } else eset = (element_set_t *)PG_GETARG_POINTER(0); /* add the value into the set */ add_element(eset, (char*)&element); MemoryContextSwitchTo(oldcontext); PG_RETURN_POINTER(eset); }
Datum quantile_append_int64_array(PG_FUNCTION_ARGS) { struct_int64 * data; MemoryContext oldcontext; MemoryContext aggcontext; GET_AGG_CONTEXT("quantile_append_int64_array", fcinfo, aggcontext); oldcontext = MemoryContextSwitchTo(aggcontext); if (PG_ARGISNULL(0)) { data = (struct_int64*)palloc(sizeof(struct_int64)); data->elements = (int64*)palloc(SLICE_SIZE*sizeof(int64)); data->nelements = SLICE_SIZE; data->next = 0; /* read the array of quantiles */ data->quantiles = array_to_double(fcinfo, PG_GETARG_ARRAYTYPE_P(2), &data->nquantiles); } else { data = (struct_int64*)PG_GETARG_POINTER(0); } /* ignore NULL values */ if (! PG_ARGISNULL(1)) { int64 element = PG_GETARG_INT64(1); if (data->next > data->nelements-1) { data->elements = (int64*)repalloc(data->elements, sizeof(int64)*(data->nelements + SLICE_SIZE)); data->nelements = data->nelements + SLICE_SIZE; } data->elements[data->next++] = element; } MemoryContextSwitchTo(oldcontext); PG_RETURN_POINTER(data); }
Datum quantile_append_numeric(PG_FUNCTION_ARGS) { struct_numeric * data; MemoryContext oldcontext; MemoryContext aggcontext; GET_AGG_CONTEXT("quantile_append_numeric", fcinfo, aggcontext); oldcontext = MemoryContextSwitchTo(aggcontext); if (PG_ARGISNULL(0)) { data = (struct_numeric*)palloc(sizeof(struct_numeric)); data->elements = (Numeric*)palloc(SLICE_SIZE*sizeof(Numeric)); data->nelements = SLICE_SIZE; data->next = 0; data->quantiles = (double*)palloc(sizeof(double)); data->quantiles[0] = PG_GETARG_FLOAT8(2); data->nquantiles = 1; } else { data = (struct_numeric*)PG_GETARG_POINTER(0); } /* ignore NULL values */ if (! PG_ARGISNULL(1)) { Numeric element = PG_GETARG_NUMERIC(1); if (data->next > data->nelements-1) { data->elements = (Numeric*)repalloc(data->elements, sizeof(Numeric)*(data->nelements + SLICE_SIZE)); data->nelements = data->nelements + SLICE_SIZE; } data->elements[data->next++] = element; } MemoryContextSwitchTo(oldcontext); PG_RETURN_POINTER(data); }
Datum count_distinct_combine(PG_FUNCTION_ARGS) { int i; char *data, *tmp, *ptr1, *ptr2, *prev; element_set_t *eset1; element_set_t *eset2; MemoryContext agg_context; MemoryContext old_context; GET_AGG_CONTEXT("count_distinct_combine", fcinfo, agg_context); eset1 = PG_ARGISNULL(0) ? NULL : (element_set_t *) PG_GETARG_POINTER(0); eset2 = PG_ARGISNULL(1) ? NULL : (element_set_t *) PG_GETARG_POINTER(1); if (eset2 == NULL) PG_RETURN_POINTER(eset1); if (eset1 == NULL) { old_context = MemoryContextSwitchTo(agg_context); eset1 = (element_set_t *)palloc(sizeof(element_set_t)); eset1->item_size = eset2->item_size; eset1->nsorted = eset2->nsorted; eset1->nall = eset2->nall; eset1->nbytes = eset2->nbytes; eset1->data = palloc(eset1->nbytes); memcpy(eset1->data, eset2->data, eset1->nbytes); MemoryContextSwitchTo(old_context); PG_RETURN_POINTER(eset1); } Assert((eset1 != NULL) && (eset2 != NULL)); Assert((eset1->item_size > 0) && (eset1->item_size == eset2->item_size)); /* make sure both states are sorted */ compact_set(eset1, false); compact_set(eset2, false); data = MemoryContextAlloc(agg_context, (eset1->nbytes + eset2->nbytes)); tmp = data; /* merge the two arrays */ ptr1 = eset1->data; ptr2 = eset2->data; prev = NULL; for (i = 0; i < eset1->nall + eset2->nall; i++) { char *element; Assert(ptr1 <= (eset1->data + eset1->nbytes)); Assert(ptr2 <= (eset2->data + eset2->nbytes)); if ((ptr1 < (eset1->data + eset1->nbytes)) && (ptr2 < (eset2->data + eset2->nbytes))) { if (memcmp(ptr1, ptr2, eset1->item_size) <= 0) { element = ptr1; ptr1 += eset1->item_size; } else { element = ptr2; ptr2 += eset1->item_size; } } else if (ptr1 < (eset1->data + eset1->nbytes)) { element = ptr1; ptr1 += eset1->item_size; } else if (ptr2 < (eset2->data + eset2->nbytes)) { element = ptr2; ptr2 += eset2->item_size; } else elog(ERROR, "unexpected"); /* * Now figure out what to do with the element - we need to compare it * to the last value, and only keep it if it's different (and it better * be greater than the last value). */ if (tmp == data) { /* first value, so just copy */ memcpy(tmp, element, eset1->item_size); prev = tmp; tmp += eset1->item_size; } else if (memcmp(prev, element, eset1->item_size) != 0) { /* not equal to the last one, so should be greater */ Assert(memcmp(prev, element, eset1->item_size) < 0); /* first value, so just copy */ memcpy(tmp, element, eset1->item_size); prev = tmp; tmp += eset1->item_size; } } /* we must have processed the input arrays completely */ Assert(ptr1 == (eset1->data + (eset1->nall * eset1->item_size))); Assert(ptr2 == (eset2->data + (eset2->nall * eset2->item_size))); /* we might have eliminated some duplicate elements */ Assert((tmp - data) <= ((eset1->nall + eset2->nall) * eset1->item_size)); pfree(eset1->data); eset1->data = data; /* and finally compute the current number of elements */ eset1->nbytes = tmp - data; eset1->nall = eset1->nbytes / eset1->item_size; eset1->nsorted = eset1->nall; PG_RETURN_POINTER(eset1); }
Datum count_distinct_elements_append(PG_FUNCTION_ARGS) { int i; element_set_t *eset = NULL; /* info for anyarray */ Oid input_type; Oid element_type; /* array data */ ArrayType *input; int ndims; int *dims; int nitems; bits8 *null_bitmap; char *arr_ptr; Datum element; /* memory contexts */ MemoryContext oldcontext; MemoryContext aggcontext; /* * If the new value is NULL, we simply return the current aggregate state * (it might be NULL, so check it). In this case we don't really care about * the types etc. * * We may still get NULL elements in the array, but to check that we would * have to walk the array, which does not qualify as cheap check. Also we * assume that there's at least one non-NULL element, and we'll walk the * array just once. It's possible we'll get empty set this way. */ if (PG_ARGISNULL(1) && PG_ARGISNULL(0)) PG_RETURN_NULL(); else if (PG_ARGISNULL(1)) PG_RETURN_DATUM(PG_GETARG_DATUM(0)); /* from now on we know the new value is not NULL */ /* get the type of array elements */ input_type = get_fn_expr_argtype(fcinfo->flinfo, 1); element_type = get_element_type(input_type); /* * parse the array contents (we know we got non-NULL value) */ input = PG_GETARG_ARRAYTYPE_P(1); ndims = ARR_NDIM(input); dims = ARR_DIMS(input); nitems = ArrayGetNItems(ndims, dims); null_bitmap = ARR_NULLBITMAP(input); arr_ptr = ARR_DATA_PTR(input); /* make sure we're running as part of aggregate function */ GET_AGG_CONTEXT("count_distinct_elements_append", fcinfo, aggcontext); oldcontext = MemoryContextSwitchTo(aggcontext); /* add all array elements to the set */ for (i = 0; i < nitems; i++) { /* ignore nulls */ if (null_bitmap && !(null_bitmap[i / 8] & (1 << (i % 8)))) continue; /* init the hash table, if needed */ if (eset == NULL) { if (PG_ARGISNULL(0)) { int16 typlen; bool typbyval; char typalign; /* get type information for the second parameter (anyelement item) */ get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); /* we can't handle varlena types yet or values passed by reference */ if ((typlen < 0) || (! typbyval)) elog(ERROR, "count_distinct_elements handles only arrays of fixed-length types passed by value"); eset = init_set(typlen, typalign, aggcontext); } else eset = (element_set_t *)PG_GETARG_POINTER(0); } element = fetch_att(arr_ptr, true, eset->item_size); add_element(eset, (char*)&element); /* advance array pointer */ arr_ptr = att_addlength_pointer(arr_ptr, eset->item_size, arr_ptr); arr_ptr = (char *) att_align_nominal(arr_ptr, eset->typalign); } MemoryContextSwitchTo(oldcontext); if (eset == NULL) PG_RETURN_NULL(); else PG_RETURN_POINTER(eset); }