diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c715ca2..e28ff68 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -920,6 +920,31 @@
        <entry><literal>width_bucket(5.35, 0.024, 10.06, 5)</literal></entry>
        <entry><literal>3</literal></entry>
       </row>
+
+      <row>
+       <entry>
+        <indexterm>
+         <primary>varwidth_bucket</primary>
+        </indexterm>
+        <literal><function>varwidth_bucket(<parameter>op</parameter> <type>numeric</type>, <parameter>thresholds</parameter> <type>numerc[]</type>)</function></literal></entry>
+       <entry><type>int</type></entry>
+       <entry>return the bucket to which <parameter>operand</> would
+       be assigned based on right-bound bucket <parameter>thresholds</>,
+       the <parameter>thresholds</> must be ordered (smallest first)</entry>
+       <entry><literal>varwidth_bucket(5.35::numeric, ARRAY[1, 3, 4, 6]::numeric[])</literal></entry>
+       <entry><literal>3</literal></entry>
+      </row>
+
+      <row>
+       <entry><literal><function>varwidth_bucket(<parameter>op</parameter> <type>dp</type>, <parameter>thresholds</parameter> <type>dp[]</type>)</function></literal></entry>
+       <entry><type>int</type></entry>
+       <entry>return the bucket to which <parameter>operand</> would
+       be assigned based on right-bound bucket <parameter>thresholds</>,
+       the <parameter>thresholds</> must be ordered (smallest first)</entry>
+       <entry><literal>varwidth_bucket(5.35::float8, ARRAY[1, 3, 4, 6]::float8[])</literal></entry>
+       <entry><literal>3</literal></entry>
+      </row>
+
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 41b3eaa..fcbb623 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -2800,6 +2800,61 @@ width_bucket_float8(PG_FUNCTION_ARGS)
 	PG_RETURN_INT32(result);
 }
 
+/*
+ * Implements the float8 version of the varwidth_bucket() function.
+ * See also varwidth_bucket_general() and varwidth_bucket_int8().
+ *
+ * 'thresholds' is an array (must be sorted from smallest to biggest value)
+ * containing right-bounds for each "bucket", varwidth_bucket() returns
+ * integer indicating the bucket number that 'operand' belongs to. An operand
+ * greater than the upper bound is assigned to an additional bucket.
+ */
+Datum
+varwidth_bucket_float8(PG_FUNCTION_ARGS)
+{
+	float8		operand = PG_GETARG_FLOAT8(0);
+	ArrayType  *thresholds_in = PG_GETARG_ARRAYTYPE_P(1);
+	float8	   *thresholds;
+	int32		left;
+	int32		mid;
+	int32		right;
+
+	/* Verify input */
+	if (ARR_NDIM(thresholds_in) != 1)
+		ereport(ERROR,
+			(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+			 errmsg("thresholds must be one dimensional array ")));
+
+	if (array_contains_nulls(thresholds_in))
+		ereport(ERROR,
+			(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+			 errmsg("thresholds array must not contain NULLs")));
+
+	/* Make sure val and thresholds use same types so we can be sure they can be compared. */
+	if (ARR_ELEMTYPE(thresholds_in) != FLOAT8OID)
+		ereport(ERROR,
+		(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+		 errmsg("thresholds array must be type float8[]")));
+
+	thresholds = (float8 *) ARR_DATA_PTR(thresholds_in);
+
+	/* Find the bucket */
+	left = 0;
+	right = ArrayGetNItems(ARR_NDIM(thresholds_in), ARR_DIMS(thresholds_in));
+	while (left < right) {
+		mid = (left + right) / 2;
+		if (operand < thresholds[mid])
+			right = mid;
+		else
+			left = mid + 1;
+	}
+
+	/* Avoid leaking memory when handed toasted input. */
+	PG_FREE_IF_COPY(thresholds_in, 1);
+
+	PG_RETURN_INT32(left);
+}
+
 /* ========== PRIVATE ROUTINES ========== */
 
 #ifndef HAVE_CBRT
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 19d0bdc..71b3363 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -35,6 +35,7 @@
 #include "utils/builtins.h"
 #include "utils/int8.h"
 #include "utils/numeric.h"
+#include "utils/typcache.h"
 
 /* ----------
  * Uncomment the following to enable compilation of dump_numeric()
@@ -1344,6 +1345,97 @@ compute_bucket(Numeric operand, Numeric bound1, Numeric bound2,
 	free_var(&operand_var);
 }
 
+
+/*
+ * Implements the numeric version of the varwidth_bucket() function.
+ * See also varwidth_bucket_float8().
+ */
+Datum
+varwidth_bucket_numeric(PG_FUNCTION_ARGS)
+{
+	Numeric		operand = PG_GETARG_NUMERIC(0);
+	ArrayType  *thresholds_in = PG_GETARG_ARRAYTYPE_P(1);
+	char	   *thresholds;
+	Oid			collation = PG_GET_COLLATION();
+	TypeCacheEntry *typentry;
+	int32		left;
+	int32		mid;
+	int32		right;
+	int			typlen;
+	bool		typbyval;
+	char		typalign;
+
+	/* Verify input */
+	if (ARR_NDIM(thresholds_in) != 1)
+		ereport(ERROR,
+			(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+			 errmsg("thresholds must be one dimensional array ")));
+
+	if (array_contains_nulls(thresholds_in))
+		ereport(ERROR,
+			(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+			 errmsg("thresholds array must not contain NULLs")));
+
+	/* Make sure val and thresholds use same types so we can be sure they can be compared. */
+	if (ARR_ELEMTYPE(thresholds_in) != NUMERICOID)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("thresholds array must be of type numeric[]")));
+
+	/* Fetch information about the input type */
+	typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+	if (typentry == NULL ||
+		typentry->type_id != NUMERICOID)
+	{
+		typentry = lookup_type_cache(NUMERICOID,
+									 TYPECACHE_TUPDESC);
+		fcinfo->flinfo->fn_extra = (void *) typentry;
+	}
+	typlen = typentry->typlen;
+	typbyval = typentry->typbyval;
+	typalign = typentry->typalign;
+
+	/* Find the bucket */
+	left = 0;
+	right = ArrayGetNItems(ARR_NDIM(thresholds_in), ARR_DIMS(thresholds_in));
+	thresholds = ARR_DATA_PTR(thresholds_in);
+
+	while (left < right) {
+		int		i;
+		Numeric		val;
+		char *ptr = thresholds;
+
+		mid = (left + right) / 2;
+		for (i = left; i < mid; i++)
+		{
+			ptr = att_addlength_pointer(ptr, typlen, ptr);
+			ptr = (char *) att_align_nominal(ptr, typalign);
+		}
+		val = DatumGetNumeric(fetch_att(ptr, typbyval, typlen));
+
+		if (cmp_numerics(operand, val) < 0)
+		{
+			right = mid;
+		}
+		else
+		{
+			/*
+			 * Move the thresholds pointer so we don't have to seek from
+			 * the beginning of array again.
+			 * */
+			thresholds = att_addlength_pointer(ptr, typlen, ptr);
+			thresholds = (char *) att_align_nominal(thresholds, typalign);
+			left = mid + 1;
+		}
+	}
+
+	/* Avoid leaking memory when handed toasted input. */
+	PG_FREE_IF_COPY(thresholds_in, 1);
+
+	PG_RETURN_INT32(left);
+}
+
+
 /* ----------------------------------------------------------------------
  *
  * Comparison functions
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a84595e..2a4d993 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -515,6 +515,8 @@ DATA(insert OID = 309 (  float84gt		   PGNSP PGUID 12 1 0 0 0 f f f t t f i 2 0
 DATA(insert OID = 310 (  float84ge		   PGNSP PGUID 12 1 0 0 0 f f f t t f i 2 0 16 "701 700" _null_ _null_ _null_ _null_ float84ge _null_ _null_ _null_ ));
 DATA(insert OID = 320 ( width_bucket	   PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 23 "701 701 701 23" _null_ _null_ _null_ _null_ width_bucket_float8 _null_ _null_ _null_ ));
 DESCR("bucket number of operand in equidepth histogram");
+DATA(insert OID = 3256 ( varwidth_bucket   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "701 1022" _null_ _null_ _null_ _null_ varwidth_bucket_float8 _null_ _null_ _null_ ));
+DESCR("bucket number of operand in the set of right-bound buckets");
 
 DATA(insert OID = 311 (  float8			   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "700" _null_ _null_ _null_ _null_	ftod _null_ _null_ _null_ ));
 DESCR("convert float4 to float8");
@@ -2298,6 +2300,8 @@ DATA(insert OID = 1980 ( numeric_div_trunc		PGNSP PGUID 12 1 0 0 0 f f f f t f i
 DESCR("trunc(x/y)");
 DATA(insert OID = 2170 ( width_bucket			PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 23 "1700 1700 1700 23" _null_ _null_ _null_ _null_ width_bucket_numeric _null_ _null_ _null_ ));
 DESCR("bucket number of operand in equidepth histogram");
+DATA(insert OID = 3257 ( varwidth_bucket		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "1700 1231" _null_ _null_ _null_ _null_ varwidth_bucket_numeric _null_ _null_ _null_ ));
+DESCR("bucket number of operand in the set of right-bound buckets");
 
 DATA(insert OID = 1747 ( time_pl_interval		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1083 "1083 1186" _null_ _null_ _null_ _null_	time_pl_interval _null_ _null_ _null_ ));
 DATA(insert OID = 1748 ( time_mi_interval		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1083 "1083 1186" _null_ _null_ _null_ _null_	time_mi_interval _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index b0a4748..2283baf 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -452,6 +452,7 @@ extern Datum float84le(PG_FUNCTION_ARGS);
 extern Datum float84gt(PG_FUNCTION_ARGS);
 extern Datum float84ge(PG_FUNCTION_ARGS);
 extern Datum width_bucket_float8(PG_FUNCTION_ARGS);
+extern Datum varwidth_bucket_float8(PG_FUNCTION_ARGS);
 
 /* dbsize.c */
 extern Datum pg_tablespace_size_oid(PG_FUNCTION_ARGS);
@@ -1036,6 +1037,7 @@ extern Datum int4_avg_accum_inv(PG_FUNCTION_ARGS);
 extern Datum int8_avg(PG_FUNCTION_ARGS);
 extern Datum int2int4_sum(PG_FUNCTION_ARGS);
 extern Datum width_bucket_numeric(PG_FUNCTION_ARGS);
+extern Datum varwidth_bucket_numeric(PG_FUNCTION_ARGS);
 extern Datum hash_numeric(PG_FUNCTION_ARGS);
 
 /* ri_triggers.c */
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 4286691..391abf1 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1706,3 +1706,51 @@ select length(md5((f1[1]).c2)) from dest;
 
 drop table dest;
 drop type textandtext;
+-- Testing for varwidth_bucket(). For convenience, we test both the
+-- numeric and float8.
+-- errors
+SELECT varwidth_bucket(5.0::numeric, ARRAY[3, 4, NULL]);
+ERROR:  thresholds array must not contain NULLs
+SELECT varwidth_bucket(5.0::numeric, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]);
+ERROR:  thresholds must be one dimensional array 
+SELECT varwidth_bucket(5.0::float8, ARRAY[3, 4, NULL]);
+ERROR:  thresholds array must not contain NULLs
+SELECT varwidth_bucket(5.0::float8, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]);
+ERROR:  thresholds must be one dimensional array 
+-- normal operation
+CREATE TABLE varwidth_bucket_test (operand_num numeric, operand_f8 float8);
+COPY varwidth_bucket_test (operand_num) FROM stdin;
+UPDATE varwidth_bucket_test SET operand_f8 = operand_num::float8;
+SELECT
+    operand_num,
+    varwidth_bucket(operand_num, ARRAY[1, 3, 5, 10]) AS wb_1,
+    varwidth_bucket(operand_f8, ARRAY[1, 3, 5, 10]) AS wb_1f,
+    varwidth_bucket(operand_num, ARRAY[0, 5.5, 9.99]) AS wb_2,
+    varwidth_bucket(operand_f8, ARRAY[0, 5.5, 9.99]) AS wb_2f,
+    varwidth_bucket(operand_num, ARRAY[-6, -5, 2]) AS wb_3,
+    varwidth_bucket(operand_f8, ARRAY[-6, -5, 2]) AS wb_3f
+    FROM varwidth_bucket_test;
+   operand_num    | wb_1 | wb_1f | wb_2 | wb_2f | wb_3 | wb_3f 
+------------------+------+-------+------+-------+------+-------
+             -5.2 |    0 |     0 |    0 |     0 |    1 |     1
+    -0.0000000001 |    0 |     0 |    0 |     0 |    2 |     2
+   0.000000000001 |    0 |     0 |    1 |     1 |    2 |     2
+                1 |    1 |     1 |    1 |     1 |    2 |     2
+ 1.99999999999999 |    1 |     1 |    1 |     1 |    2 |     2
+                2 |    1 |     1 |    1 |     1 |    3 |     3
+ 2.00000000000001 |    1 |     1 |    1 |     1 |    3 |     3
+                3 |    2 |     2 |    1 |     1 |    3 |     3
+                4 |    2 |     2 |    1 |     1 |    3 |     3
+              4.5 |    2 |     2 |    1 |     1 |    3 |     3
+                5 |    3 |     3 |    1 |     1 |    3 |     3
+              5.5 |    3 |     3 |    2 |     2 |    3 |     3
+                6 |    3 |     3 |    2 |     2 |    3 |     3
+                7 |    3 |     3 |    2 |     2 |    3 |     3
+                8 |    3 |     3 |    2 |     2 |    3 |     3
+                9 |    3 |     3 |    2 |     2 |    3 |     3
+ 9.99999999999999 |    3 |     3 |    3 |     3 |    3 |     3
+               10 |    4 |     4 |    3 |     3 |    3 |     3
+ 10.0000000000001 |    4 |     4 |    3 |     3 |    3 |     3
+(19 rows)
+
+DROP TABLE varwidth_bucket_test;
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index d9f7cbf..d6221c6 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -476,3 +476,51 @@ drop table src;
 select length(md5((f1[1]).c2)) from dest;
 drop table dest;
 drop type textandtext;
+
+-- Testing for varwidth_bucket(). For convenience, we test both the
+-- numeric and float8.
+
+-- errors
+SELECT varwidth_bucket(5.0::numeric, ARRAY[3, 4, NULL]);
+SELECT varwidth_bucket(5.0::numeric, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]);
+SELECT varwidth_bucket(5.0::float8, ARRAY[3, 4, NULL]);
+SELECT varwidth_bucket(5.0::float8, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]);
+
+-- normal operation
+CREATE TABLE varwidth_bucket_test (operand_num numeric, operand_f8 float8);
+
+COPY varwidth_bucket_test (operand_num) FROM stdin;
+-5.2
+-0.0000000001
+0.000000000001
+1
+1.99999999999999
+2
+2.00000000000001
+3
+4
+4.5
+5
+5.5
+6
+7
+8
+9
+9.99999999999999
+10
+10.0000000000001
+\.
+
+UPDATE varwidth_bucket_test SET operand_f8 = operand_num::float8;
+
+SELECT
+    operand_num,
+    varwidth_bucket(operand_num, ARRAY[1, 3, 5, 10]) AS wb_1,
+    varwidth_bucket(operand_f8, ARRAY[1, 3, 5, 10]) AS wb_1f,
+    varwidth_bucket(operand_num, ARRAY[0, 5.5, 9.99]) AS wb_2,
+    varwidth_bucket(operand_f8, ARRAY[0, 5.5, 9.99]) AS wb_2f,
+    varwidth_bucket(operand_num, ARRAY[-6, -5, 2]) AS wb_3,
+    varwidth_bucket(operand_f8, ARRAY[-6, -5, 2]) AS wb_3f
+    FROM varwidth_bucket_test;
+
+DROP TABLE varwidth_bucket_test;






