Example #1
0
// CheckValueWidth checks that the width (for strings/byte arrays) and
// scale (for decimals) of the value fits the specified column type.
// Used by INSERT and UPDATE.
func CheckValueWidth(col ColumnDescriptor, val parser.Datum) error {
	switch col.Type.Kind {
	case ColumnType_STRING:
		if v, ok := val.(*parser.DString); ok {
			if col.Type.Width > 0 && utf8.RuneCountInString(string(*v)) > int(col.Type.Width) {
				return fmt.Errorf("value too long for type %s (column %q)",
					col.Type.SQLString(), col.Name)
			}
		}
	case ColumnType_DECIMAL:
		if v, ok := val.(*parser.DDecimal); ok {
			if col.Type.Precision > 0 {
				// http://www.postgresql.org/docs/9.5/static/datatype-numeric.html
				// "If the scale of a value to be stored is greater than
				// the declared scale of the column, the system will round the
				// value to the specified number of fractional digits. Then,
				// if the number of digits to the left of the decimal point
				// exceeds the declared precision minus the declared scale, an
				// error is raised."

				if col.Type.Width > 0 {
					// Rounding half up, as per round_var() in PostgreSQL 9.5.
					v.Dec.Round(&v.Dec, inf.Scale(col.Type.Width), inf.RoundHalfUp)
				}

				// Check that the precision is not exceeded.
				maxDigitsLeft := decimal.PowerOfTenDec(int(col.Type.Precision - col.Type.Width))

				absRounded := &v.Dec
				if absRounded.Sign() == -1 {
					// Only force the allocation on negative decimals.
					absRounded = new(inf.Dec).Neg(&v.Dec)
				}
				if absRounded.Cmp(maxDigitsLeft) != -1 {
					return fmt.Errorf("too many digits for type %s (column %q)",
						col.Type.SQLString(), col.Name)
				}
			}
		}
	}
	return nil
}
Example #2
0
// CheckValueWidth checks that the width (for strings, byte arrays, and
// bit string) and scale (for decimals) of the value fits the specified
// column type. Used by INSERT and UPDATE.
func CheckValueWidth(col ColumnDescriptor, val parser.Datum) error {
	switch col.Type.Kind {
	case ColumnType_STRING:
		if v, ok := val.(*parser.DString); ok {
			if col.Type.Width > 0 && utf8.RuneCountInString(string(*v)) > int(col.Type.Width) {
				return fmt.Errorf("value too long for type %s (column %q)",
					col.Type.SQLString(), col.Name)
			}
		}
	case ColumnType_INT:
		if v, ok := val.(*parser.DInt); ok {
			if col.Type.Width > 0 {
				// https://www.postgresql.org/docs/9.5/static/datatype-bit.html
				// "bit type data must match the length n exactly; it is an error
				// to attempt to store shorter or longer bit strings. bit varying
				// data is of variable length up to the maximum length n; longer
				// strings will be rejected."
				//
				// TODO(nvanbenschoten) Because we do not propagate the "varying"
				// flag on the column type, the best we can do here is conservatively
				// assume the varying bit type and error only on longer bit strings.
				mostSignificantBit := int32(0)
				for bits := uint64(*v); bits != 0; mostSignificantBit++ {
					bits >>= 1
				}
				if mostSignificantBit > col.Type.Width {
					return fmt.Errorf("bit string too long for type %s (column %q)",
						col.Type.SQLString(), col.Name)
				}
			}
		}
	case ColumnType_DECIMAL:
		if v, ok := val.(*parser.DDecimal); ok {
			if col.Type.Precision > 0 {
				// http://www.postgresql.org/docs/9.5/static/datatype-numeric.html
				// "If the scale of a value to be stored is greater than
				// the declared scale of the column, the system will round the
				// value to the specified number of fractional digits. Then,
				// if the number of digits to the left of the decimal point
				// exceeds the declared precision minus the declared scale, an
				// error is raised."

				if col.Type.Width > 0 {
					// Rounding half up, as per round_var() in PostgreSQL 9.5.
					v.Dec.Round(&v.Dec, inf.Scale(col.Type.Width), inf.RoundHalfUp)
				}

				// Check that the precision is not exceeded.
				maxDigitsLeft := decimal.PowerOfTenDec(int(col.Type.Precision - col.Type.Width))

				absRounded := &v.Dec
				if absRounded.Sign() == -1 {
					// Only force the allocation on negative decimals.
					absRounded = new(inf.Dec).Neg(&v.Dec)
				}
				if absRounded.Cmp(maxDigitsLeft) != -1 {
					return fmt.Errorf("too many digits for type %s (column %q)",
						col.Type.SQLString(), col.Name)
				}
			}
		}
	}
	return nil
}