module DuckDB::Converter
def _decimal_to_unscaled(value, scale)
def _decimal_to_unscaled(value, scale) (value * (10**scale)).to_i end
def _decimal_width(value)
def _decimal_width(value) value.to_s('F').gsub(/[^0-9]/, '').length end
def _hugeint_lower(value)
def _hugeint_lower(value) value & LOWER_HUGEINT_MASK end
def _hugeint_upper(value)
def _hugeint_upper(value) value >> HALF_HUGEINT_BIT end
def _parse_date(value)
def _parse_date(value) case value when Date, Time value else Date.parse(value) end rescue StandardError => e raise(ArgumentError, "Cannot parse `#{value.inspect}` to Date object. #{e.message}") end
def _parse_deciaml(value)
def _parse_deciaml(value) case value when BigDecimal value else begin BigDecimal(value.to_s) rescue StandardError => e raise(ArgumentError, "Cannot parse `#{value.inspect}` to BigDecimal object. #{e.message}") end end end
def _parse_time(value)
def _parse_time(value) case value when Time value when DateTime value.to_time else Time.parse(value) end rescue StandardError => e raise(ArgumentError, "Cannot parse `#{value.inspect}` to Time object. #{e.message}") end
def _to_date(year, month, day)
def _to_date(year, month, day) Date.new(year, month, day) end
def _to_decimal_from_hugeint(width, scale, upper, lower = nil)
def _to_decimal_from_hugeint(width, scale, upper, lower = nil) v = lower.nil? ? upper : _to_hugeint_from_vector(lower, upper) _to_decimal_from_value(width, scale, v) end
def _to_decimal_from_value(_width, scale, value)
def _to_decimal_from_value(_width, scale, value) BigDecimal("#{value}e-#{scale}") end
def _to_hugeint_from_vector(lower, upper)
def _to_hugeint_from_vector(lower, upper) (upper << HALF_HUGEINT_BIT) + lower end
def _to_infinity(value)
def _to_infinity(value) if value.positive? DuckDB::Infinity::POSITIVE else DuckDB::Infinity::NEGATIVE end end
def _to_interval_from_vector(months, days, micros)
def _to_interval_from_vector(months, days, micros) Interval.new(interval_months: months, interval_days: days, interval_micros: micros) end
def _to_query_progress(percentage, rows_processed, total_rows_to_process)
def _to_query_progress(percentage, rows_processed, total_rows_to_process) DuckDB::QueryProgress.new(percentage, rows_processed, total_rows_to_process).freeze end
def _to_time(year, month, day, hour, minute, second, microsecond)
def _to_time(year, month, day, hour, minute, second, microsecond) Time.public_send( default_timezone_utc? ? :utc : :local, year, month, day, hour, minute, second, microsecond ) end
def _to_time_from_duckdb_time(hour, minute, second, microsecond)
def _to_time_from_duckdb_time(hour, minute, second, microsecond) return Time.utc(1970, 1, 1, hour, minute, second, microsecond) if default_timezone_utc? Time.parse( format( '%<hour>02d:%<minute>02d:%<second>02d.%<microsecond>06d', hour: hour, minute: minute, second: second, microsecond: microsecond ) ) end
def _to_time_from_duckdb_time_ns(nanos)
def _to_time_from_duckdb_time_ns(nanos) hour = nanos / 3_600_000_000_000 nanos %= 3_600_000_000_000 min = nanos / 60_000_000_000 nanos %= 60_000_000_000 sec = nanos / 1_000_000_000 microsecond = (nanos % 1_000_000_000) / 1_000 _to_time_from_duckdb_time(hour, min, sec, microsecond) end
def _to_time_from_duckdb_time_tz(hour, min, sec, micro, timezone)
def _to_time_from_duckdb_time_tz(hour, min, sec, micro, timezone) tz_offset = format_timezone_offset(timezone) time_str = format('%<hour>02d:%<min>02d:%<sec>02d.%<micro>06d%<tz>s', hour: hour, min: min, sec: sec, micro: micro, tz: tz_offset) Time.parse(time_str) end
def _to_time_from_duckdb_timestamp_ms(time)
def _to_time_from_duckdb_timestamp_ms(time) _to_time_from_duckdb_timestamp_s(time / 1000).then do |tm| _to_time(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec, time % 1000 * 1000) end end
def _to_time_from_duckdb_timestamp_ns(time)
def _to_time_from_duckdb_timestamp_ns(time) _to_time_from_duckdb_timestamp_s(time / 1_000_000_000).then do |tm| _to_time(tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec, time % 1_000_000_000 / 1000) end end
def _to_time_from_duckdb_timestamp_s(time)
def _to_time_from_duckdb_timestamp_s(time) if default_timezone_utc? EPOCH_UTC + time else EPOCH + time end end
def _to_time_from_duckdb_timestamp_tz(bits)
def _to_time_from_duckdb_timestamp_tz(bits) micro = bits % 1_000_000 time = EPOCH_UTC + (bits / 1_000_000) timestamp_str = format_timestamp_with_micro(time, micro) Time.parse(timestamp_str) end
def decimal_to_hugeint(value)
def decimal_to_hugeint(value) integer_value = (value * (10**value.scale)).to_i integer_to_hugeint(integer_value) rescue FloatDomainError => e raise(ArgumentError, "The argument `#{value.inspect}` must be converted to Integer. #{e.message}") end
def default_timezone_utc?
def default_timezone_utc? defined?(DuckDB.default_timezone) && DuckDB.default_timezone == :utc end
def format_timestamp_with_micro(time, micro)
def format_timestamp_with_micro(time, micro) format('%<year>04d-%<mon>02d-%<day>02d %<hour>02d:%<min>02d:%<sec>02d.%<micro>06d +0000', year: time.year, mon: time.month, day: time.day, hour: time.hour, min: time.min, sec: time.sec, micro: micro) end
def format_timezone_offset(timezone)
def format_timezone_offset(timezone) sign = timezone.negative? ? '-' : '+' offset = timezone.abs tzhour = offset / 3600 tzmin = (offset % 3600) / 60 format('%<sign>s%<hour>02d:%<min>02d', sign: sign, hour: tzhour, min: tzmin) end
def integer_to_hugeint(value)
def integer_to_hugeint(value) case value when Integer [_hugeint_lower(value), _hugeint_upper(value)] else raise(ArgumentError, "The argument `#{value.inspect}` must be Integer.") end end