diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile
index dfb0d806e4b9..ff08d17903fc 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 000000000000..9eb21f174e24
--- /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 8d3654ab7aaf..47e568cd9589 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 50427ec1170f..7797f7e08d4d 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 47787c50bd97..698750955de2 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 fd3c057f4692..32d88a0416e8 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 eec90d21ee3b..e819eacbfe80 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 0fb708074867..19355a1041fa 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.
+
+
+
+ cube + cube
+ cube
+
+
+ Computes the coordinate-wise sum of two cubes.
+
+
+
+
+
+ cube - cube
+ cube
+
+
+ Computes the coordinate-wise difference of two cubes.
+
+
+
+
+
+ cube * float8
+ cube
+
+
+ Computes the coordinate-wise multiplication of a cube by a scalar value.
+
+
+
+
+
+ float8 * cube
+ cube
+
+
+ Computes the coordinate-wise multiplication of a cube by a scalar value.
+
+
+
+
+
+ cube / float8
+ cube
+
+
+ Computes the coordinate-wise division of a cube by a scalar value.
+
+