class Innodb::DataType::DecimalType
MySQL’s Fixed-Point Type (DECIMAL), stored in InnoDB as a binary string.
Constants
- BYTES_PER_DIGIT
- MAX_DIGITS_PER_INTEGER
The value is stored as a sequence of signed big-endian integers, each representing up to 9 digits of the integral and fractional parts. The first integer of the integral part and/or the last integer of the fractional part might be compressed (or packed) and are of variable length. The remaining integers (if any) are uncompressed and 32 bits wide.
Attributes
name[R]
width[R]
Public Class Methods
new(base_type, modifiers, properties)
click to toggle source
# File lib/innodb/data_type.rb, line 108 def initialize(base_type, modifiers, properties) precision, scale = sanity_check(modifiers) integral = precision - scale @uncomp_integral = integral / MAX_DIGITS_PER_INTEGER @uncomp_fractional = scale / MAX_DIGITS_PER_INTEGER @comp_integral = integral - (@uncomp_integral * MAX_DIGITS_PER_INTEGER) @comp_fractional = scale - (@uncomp_fractional * MAX_DIGITS_PER_INTEGER) @width = (@uncomp_integral * 4) + BYTES_PER_DIGIT[@comp_integral] + (@comp_fractional * 4) + BYTES_PER_DIGIT[@comp_fractional] @name = Innodb::DataType.make_name(base_type, modifiers, properties) end
Public Instance Methods
value(data)
click to toggle source
# File lib/innodb/data_type.rb, line 120 def value(data) # Strings representing the integral and fractional parts. intg = "".dup frac = "".dup stream = StringIO.new(data) mask = sign_mask(stream) intg << get_digits(stream, mask, @comp_integral) (1..@uncomp_integral).each do intg << get_digits(stream, mask, MAX_DIGITS_PER_INTEGER) end (1..@uncomp_fractional).each do frac << get_digits(stream, mask, MAX_DIGITS_PER_INTEGER) end frac << get_digits(stream, mask, @comp_fractional) frac = "0" if frac.empty? # Convert to something resembling a string representation. str = "#{mask.to_s.chop}#{intg}.#{frac}" BigDecimal(str).to_s("F") end
Private Instance Methods
get_digits(stream, mask, digits)
click to toggle source
Return a string representing an integer with a specific number of digits.
# File lib/innodb/data_type.rb, line 176 def get_digits(stream, mask, digits) nbits = BYTES_PER_DIGIT[digits] * 8 return "" unless nbits.positive? value = (BinData.const_get("Int%dbe" % nbits).read(stream) ^ mask) # Preserve leading zeros. "%0#{digits}d" % value end
sanity_check(modifiers)
click to toggle source
Ensure width specification (if any) is compliant.
# File lib/innodb/data_type.rb, line 150 def sanity_check(modifiers) raise "Invalid width specification" unless modifiers.size <= 2 precision = modifiers.fetch(0, 10) raise "Unsupported precision for DECIMAL type" unless precision >= 1 && precision <= 65 scale = modifiers.fetch(1, 0) raise "Unsupported scale for DECIMAL type" unless scale >= 0 && scale <= 30 && scale <= precision [precision, scale] end
sign_mask(stream)
click to toggle source
The sign is encoded in the high bit of the first byte/digit. The byte might be part of a larger integer, so apply the bit-flipper and push back the byte into the stream.
# File lib/innodb/data_type.rb, line 165 def sign_mask(stream) byte = BinData::Uint8.read(stream) sign = byte & 0x80 byte.assign(byte ^ 0x80) stream.rewind byte.write(stream) stream.rewind sign.zero? ? -1 : 0 end