Skip to content

Commit

Permalink
Add vec_sub(l,r) to help with array-based subtraction between columns.
Browse files Browse the repository at this point in the history
  • Loading branch information
msqr committed Nov 30, 2021
1 parent 2411be6 commit a214882
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 1 deletion.
37 changes: 36 additions & 1 deletion aggs_for_vecs--1.3.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,41 @@ LANGUAGE c;



-- vec_sub

CREATE OR REPLACE FUNCTION
vec_sub(smallint[], smallint[])
RETURNS smallint[]
AS 'aggs_for_vecs', 'vec_sub'
LANGUAGE c;
CREATE OR REPLACE FUNCTION
vec_sub(int[], int[])
RETURNS int[]
AS 'aggs_for_vecs', 'vec_sub'
LANGUAGE c;
CREATE OR REPLACE FUNCTION
vec_sub(bigint[], bigint[])
RETURNS bigint[]
AS 'aggs_for_vecs', 'vec_sub'
LANGUAGE c;
CREATE OR REPLACE FUNCTION
vec_sub(real[], real[])
RETURNS real[]
AS 'aggs_for_vecs', 'vec_sub'
LANGUAGE c;
CREATE OR REPLACE FUNCTION
vec_sub(float[], float[])
RETURNS float[]
AS 'aggs_for_vecs', 'vec_sub'
LANGUAGE c;
CREATE OR REPLACE FUNCTION
vec_sub(numeric[], numeric[])
RETURNS numeric[]
AS 'aggs_for_vecs', 'vec_sub'
LANGUAGE c;



-- vec_trim_scale

CREATE OR REPLACE FUNCTION
Expand All @@ -50,7 +85,7 @@ LANGUAGE c;



-- vec_without_outliers
-- vec_sub

CREATE OR REPLACE FUNCTION
vec_without_outliers(smallint[], smallint[], smallint[])
Expand Down
1 change: 1 addition & 0 deletions aggs_for_vecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ PG_MODULE_MAGIC;

#include "util.c"
#include "pad_vec.c"
#include "vec_sub.c"
#include "vec_trim_scale.c"
#include "vec_without_outliers.c"
#include "vec_to_count.c"
Expand Down
37 changes: 37 additions & 0 deletions test/vec_sub.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
load test_helper

@test "int16 sub" {
result="$(query "SELECT vec_sub(ARRAY[1,2,3]::smallint[], ARRAY[3,2,1]::smallint[])")";
echo $result;
[ "$result" = "{-2,0,2}" ]
}

@test "int32 sub" {
result="$(query "SELECT vec_sub(ARRAY[1,2,3]::integer[], ARRAY[3,2,1]::integer[])")";
echo $result;
[ "$result" = "{-2,0,2}" ]
}

@test "int64 sub" {
result="$(query "SELECT vec_sub(ARRAY[1,2,3]::bigint[], ARRAY[3,2,1]::bigint[])")";
echo $result;
[ "$result" = "{-2,0,2}" ]
}

@test "float32 sub" {
result="$(query "SELECT vec_sub(ARRAY[1.0,2.2,3.4]::real[], ARRAY[3.4,2.2,1.0]::real[])")";
echo $result;
[ "$result" = "{-2.4,0,2.4}" ]
}

@test "float64 sub" {
result="$(query "SELECT vec_sub(ARRAY[1.1,2.2,3.4]::float[], ARRAY[3.4,2.2,1.1]::float[])")";
echo $result;
[ "$result" = "{-2.3,0,2.3}" ]
}

@test "numeric sub measurements" {
result="$(query "SELECT diff FROM (SELECT vec_sub(nums, lag(nums) OVER (ORDER BY sensor_id)) AS diff FROM measurements d WHERE sensor_id IN (3,4)) d WHERE d.diff IS NOT NULL")";
echo $result;
[ "$result" = "{0.00,NULL,-1.11}" ]
}
108 changes: 108 additions & 0 deletions vec_sub.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@

Datum vec_sub(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(vec_sub);

/**
* Subtract elements of two arrays.
*
* This function takes two array of n-elements and returns an array of
* n-elements where each element is difference between the input elements
* of the same index.
*/
Datum
vec_sub(PG_FUNCTION_ARGS)
{
Oid elemTypeId;
int16 elemTypeWidth;
bool elemTypeByValue;
char elemTypeAlignmentCode;
int valsLength;
ArrayType *leftArray, *rightArray, *retArray;
Datum *leftContent, *rightContent, *retContent;
bool *leftNulls, *rightNulls, *retNulls;
int i;
int dims[1];
int lbs[1];

if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}

leftArray = PG_GETARG_ARRAYTYPE_P(0);
rightArray = PG_GETARG_ARRAYTYPE_P(1);

if (ARR_NDIM(leftArray) == 0 || ARR_NDIM(rightArray) == 0) {
PG_RETURN_NULL();
}
if (ARR_NDIM(leftArray) > 1 || ARR_NDIM(rightArray) > 1) {
ereport(ERROR, (errmsg("vec_sub: one-dimensional arrays are required")));
}

elemTypeId = ARR_ELEMTYPE(leftArray);

if (elemTypeId != INT2OID &&
elemTypeId != INT4OID &&
elemTypeId != INT8OID &&
elemTypeId != FLOAT4OID &&
elemTypeId != FLOAT8OID &&
elemTypeId != NUMERICOID) {
ereport(ERROR, (errmsg("vec_sub input must be array of SMALLINT, INTEGER, BIGINT, REAL, DOUBLE PRECISION, or NUMERIC")));
}
if (rightArray && elemTypeId != ARR_ELEMTYPE(rightArray)) {
ereport(ERROR, (errmsg("vec_sub mins array must be the same type as input array")));
}

valsLength = (ARR_DIMS(leftArray))[0];
if (rightArray && valsLength != (ARR_DIMS(rightArray))[0]) {
ereport(ERROR, (errmsg("vec_sub right array must be the same length as left array")));
}

get_typlenbyvalalign(elemTypeId, &elemTypeWidth, &elemTypeByValue, &elemTypeAlignmentCode);

deconstruct_array(leftArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode,
&leftContent, &leftNulls, &valsLength);
if (rightArray) {
deconstruct_array(rightArray, elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode,
&rightContent, &rightNulls, &valsLength);
}

retContent = palloc0(sizeof(Datum) * valsLength);
retNulls = palloc0(sizeof(bool) * valsLength);

for (i = 0; i < valsLength; i++) {
if (leftNulls[i] || rightNulls[i]) {
retNulls[i] = true;
continue;
}
retNulls[i] = false;
switch(elemTypeId) {
case INT2OID:
retContent[i] = Int16GetDatum(DatumGetInt16(leftContent[i]) - DatumGetInt16(rightContent[i]));
break;
case INT4OID:
retContent[i] = Int32GetDatum(DatumGetInt32(leftContent[i]) - DatumGetInt32(rightContent[i]));
break;
case INT8OID:
retContent[i] = Int64GetDatum(DatumGetInt64(leftContent[i]) - DatumGetInt64(rightContent[i]));
break;
case FLOAT4OID:
retContent[i] = Float4GetDatum(DatumGetFloat4(leftContent[i]) - DatumGetFloat4(rightContent[i]));
break;
case FLOAT8OID:
retContent[i] = Float8GetDatum(DatumGetFloat8(leftContent[i]) - DatumGetFloat8(rightContent[i]));
break;
case NUMERICOID:
retContent[i] = DirectFunctionCall2(numeric_sub, leftContent[i], rightContent[i]);
break;
}
}

dims[0] = valsLength;
lbs[0] = 1;

retArray = construct_md_array(retContent, retNulls, 1, dims, lbs,
elemTypeId, elemTypeWidth, elemTypeByValue, elemTypeAlignmentCode);

PG_RETURN_ARRAYTYPE_P(retArray);
}

0 comments on commit a214882

Please sign in to comment.