Hi hackers!The "cube" extention is frequently used for vectors, but the current implementation lacks support for binary operators, such as +, -, *, /. The attached (fairly trivial) patch adds support for these with the required documentation and test changes.
Best Regards, Kirill Panin
From 98a8111a83a3443287061af9461b3e03e9dcb6e6 Mon Sep 17 00:00:00 2001 From: Kirill Panin <kipa...@edu.hse.ru> Date: Wed, 14 May 2025 15:30:48 +0300 Subject: [PATCH v1] Add binary operators for cubes --- contrib/cube/Makefile | 2 +- contrib/cube/cube--1.5--1.6.sql | 56 ++++++++++++++ contrib/cube/cube.c | 128 ++++++++++++++++++++++++++++++++ contrib/cube/cube.control | 2 +- contrib/cube/expected/cube.out | 61 +++++++++++++++ contrib/cube/meson.build | 1 + contrib/cube/sql/cube.sql | 12 +++ doc/src/sgml/cube.sgml | 50 +++++++++++++ 8 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 contrib/cube/cube--1.5--1.6.sql diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile index dfb0d806e4b..ff08d17903f 100644 --- a/contrib/cube/Makefile +++ b/contrib/cube/Makefile @@ -9,7 +9,7 @@ OBJS = \ EXTENSION = cube DATA = cube--1.2.sql cube--1.2--1.3.sql cube--1.3--1.4.sql cube--1.4--1.5.sql \ - cube--1.1--1.2.sql cube--1.0--1.1.sql + cube--1.1--1.2.sql cube--1.0--1.1.sql cube--1.5--1.6.sql PGFILEDESC = "cube - multidimensional cube data type" HEADERS = cubedata.h diff --git a/contrib/cube/cube--1.5--1.6.sql b/contrib/cube/cube--1.5--1.6.sql new file mode 100644 index 00000000000..9eb21f174e2 --- /dev/null +++ b/contrib/cube/cube--1.5--1.6.sql @@ -0,0 +1,56 @@ +/* contrib/cube/cube--1.5--1.6.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION cube UPDATE TO '1.6'" to load this file. \quit + +CREATE FUNCTION cube_add(cube, cube) +RETURNS cube +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION cube_sub(cube, cube) +RETURNS cube +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION cube_mul_cf(cube, float8) +RETURNS cube +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION cube_mul_fc(float8, cube) +RETURNS cube +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION cube_div(cube, float8) +RETURNS cube +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- Add coordinate-wise binary operators + +CREATE OPERATOR + ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_add, + COMMUTATOR = '+' +); + +CREATE OPERATOR - ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_sub +); + +-- Add coordinate-wise binary operators with scalars + +CREATE OPERATOR / ( + LEFTARG = cube, RIGHTARG = float8, PROCEDURE = cube_div +); + +CREATE OPERATOR * ( + LEFTARG = cube, RIGHTARG = float8, PROCEDURE = cube_mul_cf, + COMMUTATOR = '*' +); + +CREATE OPERATOR * ( + LEFTARG = float8, RIGHTARG = cube, PROCEDURE = cube_mul_fc, + COMMUTATOR = '*' +); diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c index 8d3654ab7aa..47e568cd958 100644 --- a/contrib/cube/cube.c +++ b/contrib/cube/cube.c @@ -91,6 +91,11 @@ PG_FUNCTION_INFO_V1(cube_distance); PG_FUNCTION_INFO_V1(distance_chebyshev); PG_FUNCTION_INFO_V1(cube_is_point); PG_FUNCTION_INFO_V1(cube_enlarge); +PG_FUNCTION_INFO_V1(cube_add); +PG_FUNCTION_INFO_V1(cube_sub); +PG_FUNCTION_INFO_V1(cube_div); +PG_FUNCTION_INFO_V1(cube_mul_cf); +PG_FUNCTION_INFO_V1(cube_mul_fc); /* ** For internal use only @@ -109,6 +114,8 @@ bool g_cube_internal_consistent(NDBOX *key, NDBOX *query, StrategyNumber strate */ static double distance_1D(double a1, double a2, double b1, double b2); static bool cube_is_point_internal(NDBOX *cube); +static NDBOX *cube_binop_helper(NDBOX *a, NDBOX *b); +static NDBOX *cube_alloc_shape(NDBOX *a); /***************************************************************************** @@ -1911,3 +1918,124 @@ cube_c_f8_f8(PG_FUNCTION_ARGS) PG_FREE_IF_COPY(cube, 0); PG_RETURN_NDBOX_P(result); } + +static NDBOX * +cube_alloc_shape(NDBOX *a) { + NDBOX *result; + int dim = DIM(a); + int size; + + if (IS_POINT(a)) { + size = POINT_SIZE(dim); + result = (NDBOX *) palloc0(size); + SET_POINT_BIT(result); + } else { + size = CUBE_SIZE(dim); + result = (NDBOX *) palloc0(size); + } + + SET_VARSIZE(result, size); + SET_DIM(result, dim); + + return result; +} + +NDBOX * +cube_binop_helper(NDBOX *a, NDBOX *b) +{ + if (DIM(a) != DIM(b)) + ereport(ERROR, + (errcode(ERRCODE_CARDINALITY_VIOLATION), + errmsg("cubes have different lengths: %d != %d", DIM(a), DIM(b)))); + + if (IS_POINT(a) != IS_POINT(b)) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("it's POINTless to add these cubes: %d != %d", IS_POINT(a), IS_POINT(b)))); + + return cube_alloc_shape(a); +} + +/* + * Function returns coordinate-wise sum of cubes. + */ +Datum +cube_add(PG_FUNCTION_ARGS) +{ + NDBOX *a = PG_GETARG_NDBOX_P(0); + NDBOX *b = PG_GETARG_NDBOX_P(1); + NDBOX *result = cube_binop_helper(a, b); + int i; + int n = DIM(a) * (IS_POINT(a) ? 1 : 2); + + for (i = 0; i < n; i++) + result->x[i] = a->x[i] + b->x[i]; + + PG_RETURN_NDBOX_P(result); +} + +/* + * Function returns coordinate-wise difference of cubes. + */ +Datum +cube_sub(PG_FUNCTION_ARGS) +{ + NDBOX *a = PG_GETARG_NDBOX_P(0); + NDBOX *b = PG_GETARG_NDBOX_P(1); + NDBOX *result = cube_binop_helper(a, b); + int i; + int n = DIM(a) * (IS_POINT(a) ? 1 : 2); + + for (i = 0; i < n; i++) + result->x[i] = a->x[i] - b->x[i]; + + PG_RETURN_NDBOX_P(result); +} + +/* + * Functions return scaled cube. + */ +Datum +cube_div(PG_FUNCTION_ARGS) +{ + NDBOX *a = PG_GETARG_NDBOX_P(0); + double s = PG_GETARG_FLOAT8(1); + NDBOX *result = cube_alloc_shape(a); + int i; + int n = DIM(a) * (IS_POINT(a) ? 1 : 2); + + for (i = 0; i < n; i++) + result->x[i] = a->x[i] / s; + + PG_RETURN_NDBOX_P(result); +} + +Datum +cube_mul_cf(PG_FUNCTION_ARGS) +{ + NDBOX *a = PG_GETARG_NDBOX_P(0); + double s = PG_GETARG_FLOAT8(1); + NDBOX *result = cube_alloc_shape(a); + int i; + int n = DIM(a) * (IS_POINT(a) ? 1 : 2); + + for (i = 0; i < n; i++) + result->x[i] = a->x[i] * s; + + PG_RETURN_NDBOX_P(result); +} + +Datum +cube_mul_fc(PG_FUNCTION_ARGS) +{ + double s = PG_GETARG_FLOAT8(0); + NDBOX *a = PG_GETARG_NDBOX_P(1); + NDBOX *result = cube_alloc_shape(a); + int i; + int n = DIM(a) * (IS_POINT(a) ? 1 : 2); + + for (i = 0; i < n; i++) + result->x[i] = a->x[i] * s; + + PG_RETURN_NDBOX_P(result); +} diff --git a/contrib/cube/cube.control b/contrib/cube/cube.control index 50427ec1170..7797f7e08d4 100644 --- a/contrib/cube/cube.control +++ b/contrib/cube/cube.control @@ -1,6 +1,6 @@ # cube extension comment = 'data type for multidimensional cubes' -default_version = '1.5' +default_version = '1.6' module_pathname = '$libdir/cube' relocatable = true trusted = true diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out index 47787c50bd9..698750955de 100644 --- a/contrib/cube/expected/cube.out +++ b/contrib/cube/expected/cube.out @@ -1973,3 +1973,64 @@ SELECT c~>(-4), c FROM test_cube ORDER BY c~>(-4) LIMIT 15; -- descending by upp (15 rows) RESET enable_indexscan; +-- Test of binary operators +SELECT '(-7,7)'::cube + '(-1,1)'::cube; + ?column? +---------- + (-8, 8) +(1 row) + +SELECT '(-7,7)'::cube - '(-1,1)'::cube; + ?column? +---------- + (-6, 6) +(1 row) + +SELECT '(-1,2)'::cube * 2; + ?column? +---------- + (-2, 4) +(1 row) + +SELECT '(-2,4)'::cube / 2; + ?column? +---------- + (-1, 2) +(1 row) + +SELECT 2 * '(-1,2)'::cube; + ?column? +---------- + (-2, 4) +(1 row) + +SELECT '(-7,7),(2,9)'::cube + '(-1,1),(5,12)'::cube; + ?column? +----------------- + (-8, 8),(7, 21) +(1 row) + +SELECT '(-7,7),(2,9)'::cube - '(-1,1),(5,12)'::cube; + ?column? +------------------ + (-6, 6),(-3, -3) +(1 row) + +SELECT '(-1,2),(2,9)'::cube * 2; + ?column? +----------------- + (-2, 4),(4, 18) +(1 row) + +SELECT '(-2,4),(3,7)'::cube / 2; + ?column? +-------------------- + (-1, 2),(1.5, 3.5) +(1 row) + +SELECT 2 * '(-1,2),(2,9)'::cube; + ?column? +----------------- + (-2, 4),(4, 18) +(1 row) + diff --git a/contrib/cube/meson.build b/contrib/cube/meson.build index fd3c057f469..32d88a0416e 100644 --- a/contrib/cube/meson.build +++ b/contrib/cube/meson.build @@ -40,6 +40,7 @@ install_data( 'cube--1.2--1.3.sql', 'cube--1.3--1.4.sql', 'cube--1.4--1.5.sql', + 'cube--1.5--1.6.sql', kwargs: contrib_data_args, ) diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql index eec90d21ee3..e819eacbfe8 100644 --- a/contrib/cube/sql/cube.sql +++ b/contrib/cube/sql/cube.sql @@ -436,3 +436,15 @@ SELECT c~>(-2), c FROM test_cube ORDER BY c~>(-2) LIMIT 15; -- descending by rig SELECT c~>(-3), c FROM test_cube ORDER BY c~>(-3) LIMIT 15; -- descending by lower bound SELECT c~>(-4), c FROM test_cube ORDER BY c~>(-4) LIMIT 15; -- descending by upper bound RESET enable_indexscan; + +-- Test of binary operators +SELECT '(-7,7)'::cube + '(-1,1)'::cube; +SELECT '(-7,7)'::cube - '(-1,1)'::cube; +SELECT '(-1,2)'::cube * 2; +SELECT '(-2,4)'::cube / 2; +SELECT 2 * '(-1,2)'::cube; +SELECT '(-7,7),(2,9)'::cube + '(-1,1),(5,12)'::cube; +SELECT '(-7,7),(2,9)'::cube - '(-1,1),(5,12)'::cube; +SELECT '(-1,2),(2,9)'::cube * 2; +SELECT '(-2,4),(3,7)'::cube / 2; +SELECT 2 * '(-1,2),(2,9)'::cube; diff --git a/doc/src/sgml/cube.sgml b/doc/src/sgml/cube.sgml index 0fb70807486..19355a1041f 100644 --- a/doc/src/sgml/cube.sgml +++ b/doc/src/sgml/cube.sgml @@ -218,6 +218,56 @@ Computes the Chebyshev (L-inf metric) distance between the two cubes. </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <type>cube</type> <literal>+</literal> <type>cube</type> + <returnvalue>cube</returnvalue> + </para> + <para> + Computes the coordinate-wise sum of two cubes. + </para></entry> + </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <type>cube</type> <literal>-</literal> <type>cube</type> + <returnvalue>cube</returnvalue> + </para> + <para> + Computes the coordinate-wise difference of two cubes. + </para></entry> + </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <type>cube</type> <literal>*</literal> <type>float8</type> + <returnvalue>cube</returnvalue> + </para> + <para> + Computes the coordinate-wise multiplication of a cube by a scalar value. + </para></entry> + </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <type>float8</type> <literal>*</literal> <type>cube</type> + <returnvalue>cube</returnvalue> + </para> + <para> + Computes the coordinate-wise multiplication of a cube by a scalar value. + </para></entry> + </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <type>cube</type> <literal>/</literal> <type>float8</type> + <returnvalue>cube</returnvalue> + </para> + <para> + Computes the coordinate-wise division of a cube by a scalar value. + </para></entry> + </row> </tbody> </tgroup> </table> base-commit: 2c0ed86d393670c7054d051490063de771f1791e -- 2.49.0