// 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 }
// 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 }