⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 numeric.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 5 页
字号:
static voiddump_var(const char *str, NumericVar *var){	int			i;	printf("%s: VAR w=%d d=%d ", str, var->weight, var->dscale);	switch (var->sign)	{		case NUMERIC_POS:			printf("POS");			break;		case NUMERIC_NEG:			printf("NEG");			break;		case NUMERIC_NAN:			printf("NaN");			break;		default:			printf("SIGN=0x%x", var->sign);			break;	}	for (i = 0; i < var->ndigits; i++)		printf(" %0*d", DEC_DIGITS, var->digits[i]);	printf("\n");}#endif   /* NUMERIC_DEBUG *//* ---------------------------------------------------------------------- * * Local functions follow * * In general, these do not support NaNs --- callers must eliminate * the possibility of NaN first.  (make_result() is an exception.) * * ---------------------------------------------------------------------- *//* * alloc_var() - * *	Allocate a digit buffer of ndigits digits (plus a spare digit for rounding) */static voidalloc_var(NumericVar *var, int ndigits){	digitbuf_free(var->buf);	var->buf = digitbuf_alloc(ndigits + 1);	var->buf[0] = 0;			/* spare digit for rounding */	var->digits = var->buf + 1;	var->ndigits = ndigits;}/* * free_var() - * *	Return the digit buffer of a variable to the free pool */static voidfree_var(NumericVar *var){	digitbuf_free(var->buf);	var->buf = NULL;	var->digits = NULL;	var->sign = NUMERIC_NAN;}/* * zero_var() - * *	Set a variable to ZERO. *	Note: its dscale is not touched. */static voidzero_var(NumericVar *var){	digitbuf_free(var->buf);	var->buf = NULL;	var->digits = NULL;	var->ndigits = 0;	var->weight = 0;			/* by convention; doesn't really matter */	var->sign = NUMERIC_POS;	/* anything but NAN... */}/* * set_var_from_str() * *	Parse a string and put the number into a variable */static voidset_var_from_str(const char *str, NumericVar *dest){	const char *cp = str;	bool		have_dp = FALSE;	int			i;	unsigned char *decdigits;	int			sign = NUMERIC_POS;	int			dweight = -1;	int			ddigits;	int			dscale = 0;	int			weight;	int			ndigits;	int			offset;	NumericDigit *digits;	/*	 * We first parse the string to extract decimal digits and determine	 * the correct decimal weight.	Then convert to NBASE representation.	 */	/* skip leading spaces */	while (*cp)	{		if (!isspace((unsigned char) *cp))			break;		cp++;	}	switch (*cp)	{		case '+':			sign = NUMERIC_POS;			cp++;			break;		case '-':			sign = NUMERIC_NEG;			cp++;			break;	}	if (*cp == '.')	{		have_dp = TRUE;		cp++;	}	if (!isdigit((unsigned char) *cp))		ereport(ERROR,				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),			   errmsg("invalid input syntax for type numeric: \"%s\"", str)));	decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2);	/* leading padding for digit alignment later */	memset(decdigits, 0, DEC_DIGITS);	i = DEC_DIGITS;	while (*cp)	{		if (isdigit((unsigned char) *cp))		{			decdigits[i++] = *cp++ - '0';			if (!have_dp)				dweight++;			else				dscale++;		}		else if (*cp == '.')		{			if (have_dp)				ereport(ERROR,						(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					   errmsg("invalid input syntax for type numeric: \"%s\"",							  str)));			have_dp = TRUE;			cp++;		}		else			break;	}	ddigits = i - DEC_DIGITS;	/* trailing padding for digit alignment later */	memset(decdigits + i, 0, DEC_DIGITS - 1);	/* Handle exponent, if any */	if (*cp == 'e' || *cp == 'E')	{		long		exponent;		char	   *endptr;		cp++;		exponent = strtol(cp, &endptr, 10);		if (endptr == cp)			ereport(ERROR,					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					 errmsg("invalid input syntax for type numeric: \"%s\"",							str)));		cp = endptr;		if (exponent > NUMERIC_MAX_PRECISION ||			exponent < -NUMERIC_MAX_PRECISION)			ereport(ERROR,					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					 errmsg("invalid input syntax for type numeric: \"%s\"",							str)));		dweight += (int) exponent;		dscale -= (int) exponent;		if (dscale < 0)			dscale = 0;	}	/* Should be nothing left but spaces */	while (*cp)	{		if (!isspace((unsigned char) *cp))			ereport(ERROR,					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					 errmsg("invalid input syntax for type numeric: \"%s\"",							str)));		cp++;	}	/*	 * Okay, convert pure-decimal representation to base NBASE.  First we	 * need to determine the converted weight and ndigits.	offset is the	 * number of decimal zeroes to insert before the first given digit to	 * have a correctly aligned first NBASE digit.	 */	if (dweight >= 0)		weight = (dweight + 1 + DEC_DIGITS - 1) / DEC_DIGITS - 1;	else		weight = -((-dweight - 1) / DEC_DIGITS + 1);	offset = (weight + 1) * DEC_DIGITS - (dweight + 1);	ndigits = (ddigits + offset + DEC_DIGITS - 1) / DEC_DIGITS;	alloc_var(dest, ndigits);	dest->sign = sign;	dest->weight = weight;	dest->dscale = dscale;	i = DEC_DIGITS - offset;	digits = dest->digits;	while (ndigits-- > 0)	{#if DEC_DIGITS == 4		*digits++ = ((decdigits[i] * 10 + decdigits[i + 1]) * 10 +					 decdigits[i + 2]) * 10 + decdigits[i + 3];#elif DEC_DIGITS == 2		*digits++ = decdigits[i] * 10 + decdigits[i + 1];#elif DEC_DIGITS == 1		*digits++ = decdigits[i];#else#error unsupported NBASE#endif		i += DEC_DIGITS;	}	pfree(decdigits);	/* Strip any leading/trailing zeroes, and normalize weight if zero */	strip_var(dest);}/* * set_var_from_num() - * *	Convert the packed db format into a variable */static voidset_var_from_num(Numeric num, NumericVar *dest){	int			ndigits;	ndigits = (num->varlen - NUMERIC_HDRSZ) / sizeof(NumericDigit);	alloc_var(dest, ndigits);	dest->weight = num->n_weight;	dest->sign = NUMERIC_SIGN(num);	dest->dscale = NUMERIC_DSCALE(num);	memcpy(dest->digits, num->n_data, ndigits * sizeof(NumericDigit));}/* * set_var_from_var() - * *	Copy one variable into another */static voidset_var_from_var(NumericVar *value, NumericVar *dest){	NumericDigit *newbuf;	newbuf = digitbuf_alloc(value->ndigits + 1);	newbuf[0] = 0;				/* spare digit for rounding */	memcpy(newbuf + 1, value->digits, value->ndigits * sizeof(NumericDigit));	digitbuf_free(dest->buf);	memcpy(dest, value, sizeof(NumericVar));	dest->buf = newbuf;	dest->digits = newbuf + 1;}/* * get_str_from_var() - * *	Convert a var to text representation (guts of numeric_out). *	CAUTION: var's contents may be modified by rounding! *	Returns a palloc'd string. */static char *get_str_from_var(NumericVar *var, int dscale){	char	   *str;	char	   *cp;	char	   *endcp;	int			i;	int			d;	NumericDigit dig;#if DEC_DIGITS > 1	NumericDigit d1;#endif	if (dscale < 0)		dscale = 0;	/*	 * Check if we must round up before printing the value and do so.	 */	round_var(var, dscale);	/*	 * Allocate space for the result.	 *	 * i is set to to # of decimal digits before decimal point. dscale is the	 * # of decimal digits we will print after decimal point. We may	 * generate as many as DEC_DIGITS-1 excess digits at the end, and in	 * addition we need room for sign, decimal point, null terminator.	 */	i = (var->weight + 1) * DEC_DIGITS;	if (i <= 0)		i = 1;	str = palloc(i + dscale + DEC_DIGITS + 2);	cp = str;	/*	 * Output a dash for negative values	 */	if (var->sign == NUMERIC_NEG)		*cp++ = '-';	/*	 * Output all digits before the decimal point	 */	if (var->weight < 0)	{		d = var->weight + 1;		*cp++ = '0';	}	else	{		for (d = 0; d <= var->weight; d++)		{			dig = (d < var->ndigits) ? var->digits[d] : 0;			/* In the first digit, suppress extra leading decimal zeroes */#if DEC_DIGITS == 4			{				bool		putit = (d > 0);				d1 = dig / 1000;				dig -= d1 * 1000;				putit |= (d1 > 0);				if (putit)					*cp++ = d1 + '0';				d1 = dig / 100;				dig -= d1 * 100;				putit |= (d1 > 0);				if (putit)					*cp++ = d1 + '0';				d1 = dig / 10;				dig -= d1 * 10;				putit |= (d1 > 0);				if (putit)					*cp++ = d1 + '0';				*cp++ = dig + '0';			}#elif DEC_DIGITS == 2			d1 = dig / 10;			dig -= d1 * 10;			if (d1 > 0 || d > 0)				*cp++ = d1 + '0';			*cp++ = dig + '0';#elif DEC_DIGITS == 1			*cp++ = dig + '0';#else#error unsupported NBASE#endif		}	}	/*	 * If requested, output a decimal point and all the digits that follow	 * it.	We initially put out a multiple of DEC_DIGITS digits, then	 * truncate if needed.	 */	if (dscale > 0)	{		*cp++ = '.';		endcp = cp + dscale;		for (i = 0; i < dscale; d++, i += DEC_DIGITS)		{			dig = (d >= 0 && d < var->ndigits) ? var->digits[d] : 0;#if DEC_DIGITS == 4			d1 = dig / 1000;			dig -= d1 * 1000;			*cp++ = d1 + '0';			d1 = dig / 100;			dig -= d1 * 100;			*cp++ = d1 + '0';			d1 = dig / 10;			dig -= d1 * 10;			*cp++ = d1 + '0';			*cp++ = dig + '0';#elif DEC_DIGITS == 2			d1 = dig / 10;			dig -= d1 * 10;			*cp++ = d1 + '0';			*cp++ = dig + '0';#elif DEC_DIGITS == 1			*cp++ = dig + '0';#else#error unsupported NBASE#endif		}		cp = endcp;	}	/*	 * terminate the string and return it	 */	*cp = '\0';	return str;}/* * make_result() - * *	Create the packed db numeric format in palloc()'d memory from *	a variable. */static Numericmake_result(NumericVar *var){	Numeric		result;	NumericDigit *digits = var->digits;	int			weight = var->weight;	int			sign = var->sign;	int			n;	Size		len;	if (sign == NUMERIC_NAN)	{		result = (Numeric) palloc(NUMERIC_HDRSZ);		result->varlen = NUMERIC_HDRSZ;		result->n_weight = 0;		result->n_sign_dscale = NUMERIC_NAN;		dump_numeric("make_result()", result);		return result;	}	n = var->ndigits;	/* truncate leading zeroes */	while (n > 0 && *digits == 0)	{		digits++;		weight--;		n--;	}	/* truncate trailing zeroes */	while (n > 0 && digits[n - 1] == 0)		n--;	/* If zero result, force to weight=0 and positive sign */	if (n == 0)	{		weight = 0;		sign = NUMERIC_POS;	}	/* Build the result */	len = NUMERIC_HDRSZ + n * sizeof(NumericDigit);	result = (Numeric) palloc(len);	result->varlen = len;	result->n_weight = weight;	result->n_sign_dscale = sign | (var->dscale & NUMERIC_DSCALE_MASK);	memcpy(result->n_data, digits, n * sizeof(NumericDigit));	/* Check for overflow of int16 fields */	if (result->n_weight != weight ||		NUMERIC_DSCALE(result) != var->dscale)		ereport(ERROR,				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),				 errmsg("value overflows numeric format")));	dump_numeric("make_result()", result);	return result;}/* * apply_typmod() - * *	Do bounds checking and rounding according to the attributes *	typmod field. */static voidapply_typmod(NumericVar *var, int32 typmod){	int			precision;	int			scale;	int			maxdigits;	int			ddigits;	int			i;	/* Do nothing if we have a default typmod (-1) */	if (typmod < (int32) (VARHDRSZ))		return;	typmod -= VARHDRSZ;	precision = (typmod >> 16) & 0xffff;	scale = typmod & 0xffff;	maxdigits = precision - scale;	/* Round to target scale (and set var->dscale) */	round_var(var, scale);	/*	 * Check for overflow - note we can't do this before rounding, because	 * rounding could raise the weight.  Also note that the var's weight	 * could be inflated by leading zeroes, which will be stripped before	 * storage but perhaps might not have been yet. In any case, we must	 * recognize a true zero, whose weight doesn't mean anything.	 */	ddigits = (var->weight + 1) * DEC_DIGITS;	if (ddigits > maxdigits)	{		/* Determine true weight; and check for all-zero result */		for (i = 0; i < var->ndigits; i++)		{			NumericDigit dig = var->digits[i];			if (dig)			{				/* Adjust for any high-order decimal zero digits */#if DEC_DIGITS == 4				if (dig < 10)					ddigits -= 3;				else if (dig < 100)					ddigits -= 2;				else if (dig < 1000)					ddigits -= 1;#elif DEC_DIGITS == 2				if (dig < 10)					ddigits -= 1;#elif DEC_DIGITS == 1				/* no adjustment */#else#error unsupported NBASE#endif				if (ddigits > maxdigits)					ereport(ERROR,							(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),							 errmsg("numeric field overflow"),							 errdetail("The absolute value is greater than or equal to 10^%d for field with precision %d, scale %d.",									   ddigits - 1, precision, scale)));				break;			}			ddigits -= DEC_DIGITS;		}	}}/* * Convert numeric to int8, rounding if needed. * * If overflow, return FALSE (no error

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -