diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 1fa0fc6c2a3fa3ad0f09d7222d59ebfbd649f1fe..0811a282db2ee0a8fe00bf60d584a86a5dada0dc 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.426 2008/04/04 16:57:21 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.427 2008/04/04 18:45:36 tgl Exp $ -->
 
  <chapter id="functions">
   <title>Functions and Operators</title>
@@ -617,6 +617,9 @@
    <indexterm>
     <primary>degrees</primary>
    </indexterm>
+   <indexterm>
+    <primary>div</primary>
+   </indexterm>
    <indexterm>
     <primary>exp</primary>
    </indexterm>
@@ -717,6 +720,15 @@
        <entry><literal>28.6478897565412</literal></entry>
       </row>
 
+      <row>
+       <entry><literal><function>div</function>(<parameter>y</parameter> <type>numeric</>,
+        <parameter>x</parameter> <type>numeric</>)</literal></entry>
+       <entry><type>numeric</></entry>
+       <entry>integer quotient of <parameter>y</parameter>/<parameter>x</parameter></entry>
+       <entry><literal>div(9,4)</literal></entry>
+       <entry><literal>2</literal></entry>
+      </row>
+
       <row>
        <entry><literal><function>exp</function>(<type>dp</type> or <type>numeric</type>)</literal></entry>
        <entry>(same as input)</entry>
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 2ebfee52e4559a758210d86e6327c29ddab519fd..86765d5d5325f686046f46d152625f181a1df303 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -14,7 +14,7 @@
  * Copyright (c) 1998-2008, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.108 2008/01/01 19:45:52 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.109 2008/04/04 18:45:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,7 +53,7 @@
  * NBASE that's less than sqrt(INT_MAX), in practice we are only interested
  * in NBASE a power of ten, so that I/O conversions and decimal rounding
  * are easy.  Also, it's actually more efficient if NBASE is rather less than
- * sqrt(INT_MAX), so that there is "headroom" for mul_var and div_var to
+ * sqrt(INT_MAX), so that there is "headroom" for mul_var and div_var_fast to
  * postpone processing carries.
  * ----------
  */
@@ -90,6 +90,10 @@ typedef int16 NumericDigit;
 
 
 /* ----------
+ * NumericVar is the format we use for arithmetic.  The digit-array part
+ * is the same as the NumericData storage format, but the header is more
+ * complex.
+ *
  * The value represented by a NumericVar is determined by the sign, weight,
  * ndigits, and digits[] array.
  * Note: the first digit of a NumericVar's value is assumed to be multiplied
@@ -100,7 +104,7 @@ typedef int16 NumericDigit;
  * NumericVar.	digits points at the first digit in actual use (the one
  * with the specified weight).	We normally leave an unused digit or two
  * (preset to zeroes) between buf and digits, so that there is room to store
- * a carry out of the top digit without special pushups.  We just need to
+ * a carry out of the top digit without reallocating space.  We just need to
  * decrement digits (and increment weight) to make room for the carry digit.
  * (There is no such extra space in a numeric value stored in the database,
  * only in a NumericVar in memory.)
@@ -265,6 +269,8 @@ static void mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result,
 		int rscale);
 static void div_var(NumericVar *var1, NumericVar *var2, NumericVar *result,
 		int rscale, bool round);
+static void div_var_fast(NumericVar *var1, NumericVar *var2, NumericVar *result,
+		int rscale, bool round);
 static int	select_div_scale(NumericVar *var1, NumericVar *var2);
 static void mod_var(NumericVar *var1, NumericVar *var2, NumericVar *result);
 static void ceil_var(NumericVar *var, NumericVar *result);
@@ -1419,6 +1425,52 @@ numeric_div(PG_FUNCTION_ARGS)
 }
 
 
+/*
+ * numeric_div_trunc() -
+ *
+ *	Divide one numeric into another, truncating the result to an integer
+ */
+Datum
+numeric_div_trunc(PG_FUNCTION_ARGS)
+{
+	Numeric		num1 = PG_GETARG_NUMERIC(0);
+	Numeric		num2 = PG_GETARG_NUMERIC(1);
+	NumericVar	arg1;
+	NumericVar	arg2;
+	NumericVar	result;
+	Numeric		res;
+
+	/*
+	 * Handle NaN
+	 */
+	if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
+		PG_RETURN_NUMERIC(make_result(&const_nan));
+
+	/*
+	 * Unpack the arguments
+	 */
+	init_var(&arg1);
+	init_var(&arg2);
+	init_var(&result);
+
+	set_var_from_num(num1, &arg1);
+	set_var_from_num(num2, &arg2);
+
+	/*
+	 * Do the divide and return the result
+	 */
+	div_var(&arg1, &arg2, &result, 0, false);
+
+	res = make_result(&result);
+
+	free_var(&arg1);
+	free_var(&arg2);
+	free_var(&result);
+
+	PG_RETURN_NUMERIC(res);
+}
+
+
 /*
  * numeric_mod() -
  *
@@ -4036,12 +4088,291 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result,
 /*
  * div_var() -
  *
- *	Division on variable level. Quotient of var1 / var2 is stored
- *	in result.	Result is rounded to no more than rscale fractional digits.
+ *	Division on variable level. Quotient of var1 / var2 is stored in result.
+ *	The quotient is figured to exactly rscale fractional digits.
+ *	If round is true, it is rounded at the rscale'th digit; if false, it
+ *	is truncated (towards zero) at that digit.
  */
 static void
 div_var(NumericVar *var1, NumericVar *var2, NumericVar *result,
 		int rscale, bool round)
+{
+	int			div_ndigits;
+	int			res_ndigits;
+	int			res_sign;
+	int			res_weight;
+	int			carry;
+	int			borrow;
+	int			divisor1;
+	int			divisor2;
+	NumericDigit *dividend;
+	NumericDigit *divisor;
+	NumericDigit *res_digits;
+	int			i;
+	int			j;
+
+	/* copy these values into local vars for speed in inner loop */
+	int			var1ndigits = var1->ndigits;
+	int			var2ndigits = var2->ndigits;
+
+	/*
+	 * First of all division by zero check; we must not be handed an
+	 * unnormalized divisor.
+	 */
+	if (var2ndigits == 0 || var2->digits[0] == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_DIVISION_BY_ZERO),
+				 errmsg("division by zero")));
+
+	/*
+	 * Now result zero check
+	 */
+	if (var1ndigits == 0)
+	{
+		zero_var(result);
+		result->dscale = rscale;
+		return;
+	}
+
+	/*
+	 * Determine the result sign, weight and number of digits to calculate.
+	 * The weight figured here is correct if the emitted quotient has no
+	 * leading zero digits; otherwise strip_var() will fix things up.
+	 */
+	if (var1->sign == var2->sign)
+		res_sign = NUMERIC_POS;
+	else
+		res_sign = NUMERIC_NEG;
+	res_weight = var1->weight - var2->weight;
+	/* The number of accurate result digits we need to produce: */
+	res_ndigits = res_weight + 1 + (rscale + DEC_DIGITS - 1) / DEC_DIGITS;
+	/* ... but always at least 1 */
+	res_ndigits = Max(res_ndigits, 1);
+	/* If rounding needed, figure one more digit to ensure correct result */
+	if (round)
+		res_ndigits++;
+	/*
+	 * The working dividend normally requires res_ndigits + var2ndigits
+	 * digits, but make it at least var1ndigits so we can load all of var1
+	 * into it.  (There will be an additional digit dividend[0] in the
+	 * dividend space, but for consistency with Knuth's notation we don't
+	 * count that in div_ndigits.)
+	 */
+	div_ndigits = res_ndigits + var2ndigits;
+	div_ndigits = Max(div_ndigits, var1ndigits);
+
+	/*
+	 * We need a workspace with room for the working dividend (div_ndigits+1
+	 * digits) plus room for the possibly-normalized divisor (var2ndigits
+	 * digits).  It is convenient also to have a zero at divisor[0] with
+	 * the actual divisor data in divisor[1 .. var2ndigits].  Transferring the
+	 * digits into the workspace also allows us to realloc the result (which
+	 * might be the same as either input var) before we begin the main loop.
+	 * Note that we use palloc0 to ensure that divisor[0], dividend[0], and
+	 * any additional dividend positions beyond var1ndigits, start out 0.
+	 */
+	dividend = (NumericDigit *)
+		palloc0((div_ndigits + var2ndigits + 2) * sizeof(NumericDigit));
+	divisor = dividend + (div_ndigits + 1);
+	memcpy(dividend + 1, var1->digits, var1ndigits * sizeof(NumericDigit));
+	memcpy(divisor + 1, var2->digits, var2ndigits * sizeof(NumericDigit));
+
+	/*
+	 * Now we can realloc the result to hold the generated quotient digits.
+	 */
+	alloc_var(result, res_ndigits);
+	res_digits = result->digits;
+
+	if (var2ndigits == 1)
+	{
+		/*
+		 * If there's only a single divisor digit, we can use a fast path
+		 * (cf. Knuth section 4.3.1 exercise 16).
+		 */
+		divisor1 = divisor[1];
+		carry = 0;
+		for (i = 0; i < res_ndigits; i++)
+		{
+			carry = carry * NBASE + dividend[i + 1];
+			res_digits[i] = carry / divisor1;
+			carry = carry % divisor1;
+		}
+	}
+	else
+	{
+		/*
+		 * The full multiple-place algorithm is taken from Knuth volume 2,
+		 * Algorithm 4.3.1D.
+		 *
+		 * We need the first divisor digit to be >= NBASE/2.  If it isn't,
+		 * make it so by scaling up both the divisor and dividend by the
+		 * factor "d".  (The reason for allocating dividend[0] above is to
+		 * leave room for possible carry here.)
+		 */
+		if (divisor[1] < HALF_NBASE)
+		{
+			int		d = NBASE / (divisor[1] + 1);
+
+			carry = 0;
+			for (i = var2ndigits; i > 0; i--)
+			{
+				carry += divisor[i] * d;
+				divisor[i] = carry % NBASE;
+				carry = carry / NBASE;
+			}
+			Assert(carry == 0);
+			carry = 0;
+			/* at this point only var1ndigits of dividend can be nonzero */
+			for (i = var1ndigits; i >= 0; i--)
+			{
+				carry += dividend[i] * d;
+				dividend[i] = carry % NBASE;
+				carry = carry / NBASE;
+			}
+			Assert(carry == 0);
+			Assert(divisor[1] >= HALF_NBASE);
+		}
+		/* First 2 divisor digits are used repeatedly in main loop */
+		divisor1 = divisor[1];
+		divisor2 = divisor[2];
+
+		/*
+		 * Begin the main loop.  Each iteration of this loop produces the
+		 * j'th quotient digit by dividing dividend[j .. j + var2ndigits]
+		 * by the divisor; this is essentially the same as the common manual
+		 * procedure for long division.
+		 */
+		for (j = 0; j < res_ndigits; j++)
+		{
+			/* Estimate quotient digit from the first two dividend digits */
+			int		next2digits = dividend[j] * NBASE + dividend[j+1];
+			int		qhat;
+
+			/*
+			 * If next2digits are 0, then quotient digit must be 0 and there's
+			 * no need to adjust the working dividend.  It's worth testing
+			 * here to fall out ASAP when processing trailing zeroes in
+			 * a dividend.
+			 */
+			if (next2digits == 0)
+			{
+				res_digits[j] = 0;
+				continue;
+			}
+
+			if (dividend[j] == divisor1)
+				qhat = NBASE - 1;
+			else
+				qhat = next2digits / divisor1;
+			/*
+			 * Adjust quotient digit if it's too large.  Knuth proves that
+			 * after this step, the quotient digit will be either correct
+			 * or just one too large.  (Note: it's OK to use dividend[j+2]
+			 * here because we know the divisor length is at least 2.)
+			 */
+			while (divisor2 * qhat >
+				   (next2digits - qhat * divisor1) * NBASE + dividend[j+2])
+				qhat--;
+
+			/* As above, need do nothing more when quotient digit is 0 */
+			if (qhat > 0)
+			{
+				/*
+				 * Multiply the divisor by qhat, and subtract that from the
+				 * working dividend.  "carry" tracks the multiplication,
+				 * "borrow" the subtraction (could we fold these together?)
+				 */
+				carry = 0;
+				borrow = 0;
+				for (i = var2ndigits; i >= 0; i--)
+				{
+					carry += divisor[i] * qhat;
+					borrow -= carry % NBASE;
+					carry = carry / NBASE;
+					borrow += dividend[j + i];
+					if (borrow < 0)
+					{
+						dividend[j + i] = borrow + NBASE;
+						borrow = -1;
+					}
+					else
+					{
+						dividend[j + i] = borrow;
+						borrow = 0;
+					}
+				}
+				Assert(carry == 0);
+
+				/*
+				 * If we got a borrow out of the top dividend digit, then
+				 * indeed qhat was one too large.  Fix it, and add back the
+				 * divisor to correct the working dividend.  (Knuth proves
+				 * that this will occur only about 3/NBASE of the time; hence,
+				 * it's a good idea to test this code with small NBASE to be
+				 * sure this section gets exercised.)
+				 */
+				if (borrow)
+				{
+					qhat--;
+					carry = 0;
+					for (i = var2ndigits; i >= 0; i--)
+					{
+						carry += dividend[j + i] + divisor[i];
+						if (carry >= NBASE)
+						{
+							dividend[j + i] = carry - NBASE;
+							carry = 1;
+						}
+						else
+						{
+							dividend[j + i] = carry;
+							carry = 0;
+						}
+					}
+					/* A carry should occur here to cancel the borrow above */
+					Assert(carry == 1);
+				}
+			}
+
+			/* And we're done with this quotient digit */
+			res_digits[j] = qhat;
+		}
+	}
+
+	pfree(dividend);
+
+	/*
+	 * Finally, round or truncate the result to the requested precision.
+	 */
+	result->weight = res_weight;
+	result->sign = res_sign;
+
+	/* Round or truncate to target rscale (and set result->dscale) */
+	if (round)
+		round_var(result, rscale);
+	else
+		trunc_var(result, rscale);
+
+	/* Strip leading and trailing zeroes */
+	strip_var(result);
+}
+
+
+/*
+ * div_var_fast() -
+ *
+ *	This has the same API as div_var, but is implemented using the division
+ *	algorithm from the "FM" library, rather than Knuth's schoolbook-division
+ *	approach.  This is significantly faster but can produce inaccurate
+ *	results, because it sometimes has to propagate rounding to the left,
+ *	and so we can never be entirely sure that we know the requested digits
+ *	exactly.  We compute DIV_GUARD_DIGITS extra digits, but there is
+ *	no certainty that that's enough.  We use this only in the transcendental
+ *	function calculation routines, where everything is approximate anyway.
+ */
+static void
+div_var_fast(NumericVar *var1, NumericVar *var2, NumericVar *result,
+			 int rscale, bool round)
 {
 	int			div_ndigits;
 	int			res_sign;
@@ -4367,30 +4698,21 @@ static void
 mod_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
 {
 	NumericVar	tmp;
-	int			rscale;
 
 	init_var(&tmp);
 
 	/* ---------
 	 * We do this using the equation
 	 *		mod(x,y) = x - trunc(x/y)*y
-	 * We set rscale the same way numeric_div and numeric_mul do
-	 * to get the right answer from the equation.  The final result,
-	 * however, need not be displayed to more precision than the inputs.
+	 * div_var can be persuaded to give us trunc(x/y) directly.
 	 * ----------
 	 */
-	rscale = select_div_scale(var1, var2);
+	div_var(var1, var2, &tmp, 0, false);
 
-	div_var(var1, var2, &tmp, rscale, false);
-
-	trunc_var(&tmp, 0);
-
-	mul_var(var2, &tmp, &tmp, var2->dscale + tmp.dscale);
+	mul_var(var2, &tmp, &tmp, var2->dscale);
 
 	sub_var(var1, &tmp, result);
 
-	round_var(result, Max(var1->dscale, var2->dscale));
-
 	free_var(&tmp);
 }
 
@@ -4497,7 +4819,7 @@ sqrt_var(NumericVar *arg, NumericVar *result, int rscale)
 
 	for (;;)
 	{
-		div_var(&tmp_arg, result, &tmp_val, local_rscale, true);
+		div_var_fast(&tmp_arg, result, &tmp_val, local_rscale, true);
 
 		add_var(result, &tmp_val, result);
 		mul_var(result, &const_zero_point_five, result, local_rscale);
@@ -4587,7 +4909,7 @@ exp_var(NumericVar *arg, NumericVar *result, int rscale)
 
 	/* Compensate for input sign, and round to requested rscale */
 	if (xneg)
-		div_var(&const_one, result, result, rscale, true);
+		div_var_fast(&const_one, result, result, rscale, true);
 	else
 		round_var(result, rscale);
 
@@ -4652,7 +4974,7 @@ exp_var_internal(NumericVar *arg, NumericVar *result, int rscale)
 		add_var(&ni, &const_one, &ni);
 		mul_var(&xpow, &x, &xpow, local_rscale);
 		mul_var(&ifac, &ni, &ifac, 0);
-		div_var(&xpow, &ifac, &elem, local_rscale, true);
+		div_var_fast(&xpow, &ifac, &elem, local_rscale, true);
 
 		if (elem.ndigits == 0)
 			break;
@@ -4736,7 +5058,7 @@ ln_var(NumericVar *arg, NumericVar *result, int rscale)
 	 */
 	sub_var(&x, &const_one, result);
 	add_var(&x, &const_one, &elem);
-	div_var(result, &elem, result, local_rscale, true);
+	div_var_fast(result, &elem, result, local_rscale, true);
 	set_var_from_var(result, &xx);
 	mul_var(result, result, &x, local_rscale);
 
@@ -4746,7 +5068,7 @@ ln_var(NumericVar *arg, NumericVar *result, int rscale)
 	{
 		add_var(&ni, &const_two, &ni);
 		mul_var(&xx, &x, &xx, local_rscale);
-		div_var(&xx, &ni, &elem, local_rscale, true);
+		div_var_fast(&xx, &ni, &elem, local_rscale, true);
 
 		if (elem.ndigits == 0)
 			break;
@@ -4816,7 +5138,7 @@ log_var(NumericVar *base, NumericVar *num, NumericVar *result)
 	/* Select scale for division result */
 	rscale = select_div_scale(&ln_num, &ln_base);
 
-	div_var(&ln_num, &ln_base, result, rscale, true);
+	div_var_fast(&ln_num, &ln_base, result, rscale, true);
 
 	free_var(&ln_num);
 	free_var(&ln_base);
@@ -4990,7 +5312,7 @@ power_var_int(NumericVar *base, int exp, NumericVar *result, int rscale)
 
 	/* Compensate for input sign, and round to requested rscale */
 	if (neg)
-		div_var(&const_one, result, result, rscale, true);
+		div_var_fast(&const_one, result, result, rscale, true);
 	else
 		round_var(result, rscale);
 }
@@ -5361,8 +5683,8 @@ round_var(NumericVar *var, int rscale)
 /*
  * trunc_var
  *
- * Truncate the value of a variable at rscale decimal digits after the
- * decimal point.  NOTE: we allow rscale < 0 here, implying
+ * Truncate (towards zero) the value of a variable at rscale decimal digits
+ * after the decimal point.  NOTE: we allow rscale < 0 here, implying
  * truncation before the decimal point.
  */
 static void
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 009cf8abf664ff9d4de1ecbe25e1477520dd56df..f4821984e91d0d2d9466dd74235b1cc841aa86ac 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.444 2008/03/23 00:24:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.445 2008/04/04 18:45:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200803222
+#define CATALOG_VERSION_NO	200804041
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index fba36a018d312ec249ce40bff99f13b118924436..2abaeea211efcca1fd6a50cfcac2caa65e896977 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.486 2008/04/04 16:57:21 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.487 2008/04/04 18:45:36 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -1115,7 +1115,7 @@ DESCR("does not match LIKE expression");
 DATA(insert OID =  860 (  bpchar		   PGNSP PGUID 12 1 0 f f t f i 1 1042 "18" _null_ _null_ _null_	char_bpchar - _null_ _null_ ));
 DESCR("convert char to char()");
 
-DATA(insert OID = 861 ( current_database	   PGNSP PGUID 12 1 0 f f t f i 0 19 "" _null_ _null_ _null_ current_database - _null_ _null_ ));
+DATA(insert OID = 861 ( current_database	   PGNSP PGUID 12 1 0 f f t f s 0 19 "" _null_ _null_ _null_ current_database - _null_ _null_ ));
 DESCR("returns the current database");
 DATA(insert OID = 817 (  current_query        PGNSP PGUID 12 1 0 f f f f v 0 25  "" _null_ _null_ _null_  current_query - _null_ _null_ ));
 DESCR("returns the currently executing query");
@@ -2573,6 +2573,10 @@ DATA(insert OID = 1745 ( float4					PGNSP PGUID 12 1 0 f f t f i 1 700 "1700" _n
 DESCR("(internal)");
 DATA(insert OID = 1746 ( float8					PGNSP PGUID 12 1 0 f f t f i 1 701 "1700" _null_ _null_ _null_	numeric_float8 - _null_ _null_ ));
 DESCR("(internal)");
+DATA(insert OID = 1973 ( div					PGNSP PGUID 12 1 0 f f t f i 2 1700 "1700 1700" _null_ _null_ _null_	numeric_div_trunc - _null_ _null_ ));
+DESCR("trunc(x/y)");
+DATA(insert OID = 1980 ( numeric_div_trunc		PGNSP PGUID 12 1 0 f f t f i 2 1700 "1700 1700" _null_ _null_ _null_	numeric_div_trunc - _null_ _null_ ));
+DESCR("trunc(x/y)");
 DATA(insert OID = 2170 ( width_bucket			PGNSP PGUID 12 1 0 f f t f i 4 23 "1700 1700 1700 23" _null_ _null_ _null_	width_bucket_numeric - _null_ _null_ ));
 DESCR("bucket number of operand in equidepth histogram");
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index f80802f9bffdf0875cdb171a7bbc678a201ee5e9..c14fcab72e854ef2242cd13673e0542bb0ffa343 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.311 2008/04/04 16:57:21 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.312 2008/04/04 18:45:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -845,6 +845,7 @@ extern Datum numeric_add(PG_FUNCTION_ARGS);
 extern Datum numeric_sub(PG_FUNCTION_ARGS);
 extern Datum numeric_mul(PG_FUNCTION_ARGS);
 extern Datum numeric_div(PG_FUNCTION_ARGS);
+extern Datum numeric_div_trunc(PG_FUNCTION_ARGS);
 extern Datum numeric_mod(PG_FUNCTION_ARGS);
 extern Datum numeric_inc(PG_FUNCTION_ARGS);
 extern Datum numeric_smaller(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out
index 7840990bf7dfc0e2dec02fb6e6f9334a7bad7c69..1c5047455e89b22508aa2af8145bdfac12988f2f 100644
--- a/src/test/regress/expected/numeric.out
+++ b/src/test/regress/expected/numeric.out
@@ -1260,3 +1260,84 @@ SELECT * FROM num_input_test;
  -555.50
 (5 rows)
 
+--
+-- Test some corner cases for division
+--
+select 999999999999999999999::numeric/1000000000000000000000;
+        ?column?        
+------------------------
+ 1.00000000000000000000
+(1 row)
+
+select div(999999999999999999999::numeric,1000000000000000000000);
+ div 
+-----
+   0
+(1 row)
+
+select mod(999999999999999999999::numeric,1000000000000000000000);
+          mod          
+-----------------------
+ 999999999999999999999
+(1 row)
+
+select div(-9999999999999999999999::numeric,1000000000000000000000);
+ div 
+-----
+  -9
+(1 row)
+
+select mod(-9999999999999999999999::numeric,1000000000000000000000);
+          mod           
+------------------------
+ -999999999999999999999
+(1 row)
+
+select div(-9999999999999999999999::numeric,1000000000000000000000)*1000000000000000000000 + mod(-9999999999999999999999::numeric,1000000000000000000000);
+        ?column?         
+-------------------------
+ -9999999999999999999999
+(1 row)
+
+select mod (70.0,70) ;
+ mod 
+-----
+ 0.0
+(1 row)
+
+select div (70.0,70) ;
+ div 
+-----
+   1
+(1 row)
+
+select 70.0 / 70 ;
+        ?column?        
+------------------------
+ 1.00000000000000000000
+(1 row)
+
+select 12345678901234567890 % 123;
+ ?column? 
+----------
+       78
+(1 row)
+
+select 12345678901234567890 / 123;
+      ?column?      
+--------------------
+ 100371373180768845
+(1 row)
+
+select div(12345678901234567890, 123);
+        div         
+--------------------
+ 100371373180768844
+(1 row)
+
+select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123;
+       ?column?       
+----------------------
+ 12345678901234567890
+(1 row)
+
diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql
index dc1452f9ef0a7c08770945742a0d890c4c061500..9fd6bba31eeee8fdf7806f5fe5c8bf0bf65406f4 100644
--- a/src/test/regress/sql/numeric.sql
+++ b/src/test/regress/sql/numeric.sql
@@ -805,3 +805,21 @@ INSERT INTO num_input_test(n1) VALUES ('');
 INSERT INTO num_input_test(n1) VALUES (' N aN ');
 
 SELECT * FROM num_input_test;
+
+--
+-- Test some corner cases for division
+--
+
+select 999999999999999999999::numeric/1000000000000000000000;
+select div(999999999999999999999::numeric,1000000000000000000000);
+select mod(999999999999999999999::numeric,1000000000000000000000);
+select div(-9999999999999999999999::numeric,1000000000000000000000);
+select mod(-9999999999999999999999::numeric,1000000000000000000000);
+select div(-9999999999999999999999::numeric,1000000000000000000000)*1000000000000000000000 + mod(-9999999999999999999999::numeric,1000000000000000000000);
+select mod (70.0,70) ;
+select div (70.0,70) ;
+select 70.0 / 70 ;
+select 12345678901234567890 % 123;
+select 12345678901234567890 / 123;
+select div(12345678901234567890, 123);
+select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123;