/usr/lib/ruby/vendor_ruby/sequel/extensions/pg_interval.rb is in ruby-sequel 4.15.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | # The pg_interval extension adds support for PostgreSQL's interval type.
#
# This extension integrates with Sequel's native postgres and jdbc/postgresql
# adapters, so that when interval type values are retrieved, they are parsed and returned
# as instances of ActiveSupport::Duration.
#
# In addition to the parser, this extension adds literalizers for
# ActiveSupport::Duration that use the standard Sequel literalization
# callbacks, so they work on all adapters.
#
# If you would like to use interval columns in your model objects, you
# probably want to modify the typecasting so that it
# recognizes and correctly handles the interval columns, which you can
# do by:
#
# DB.extension :pg_interval
#
# If you are not using the native postgres or jdbc/postgresql adapters and are using interval
# types as model column values you probably should use the
# pg_typecast_on_load plugin if the column values are returned as a string.
#
# This extension integrates with the pg_array extension. If you plan
# to use arrays of interval types, load the pg_array extension before the
# pg_interval extension:
#
# DB.extension :pg_array, :pg_interval
#
# The parser this extension uses requires that IntervalStyle for PostgreSQL
# is set to postgres (the default setting). If IntervalStyle is changed from
# the default setting, the parser will probably not work. The parser used is
# very simple, and is only designed to parse PostgreSQL's default output
# format, it is not designed to support all input formats that PostgreSQL
# supports.
#
# See the {schema modification guide}[rdoc-ref:doc/schema_modification.rdoc]
# for details on using interval columns in CREATE/ALTER TABLE statements.
require 'active_support/duration'
Sequel.require 'adapters/utils/pg_types'
module Sequel
module Postgres
module IntervalDatabaseMethods
EMPTY_INTERVAL = '0'.freeze
DURATION_UNITS = [:years, :months, :days, :minutes, :seconds].freeze
# Return an unquoted string version of the duration object suitable for
# use as a bound variable.
def self.literal_duration(duration)
h = Hash.new(0)
duration.parts.each{|unit, value| h[unit] += value}
s = ''
DURATION_UNITS.each do |unit|
if (v = h[unit]) != 0
s << "#{v.is_a?(Integer) ? v : sprintf('%0.6f', v)} #{unit} "
end
end
if s.empty?
EMPTY_INTERVAL
else
s
end
end
# Creates callable objects that convert strings into ActiveSupport::Duration instances.
class Parser
# Regexp that parses the full range of PostgreSQL interval type output.
PARSER = /\A([+-]?\d+ years?\s?)?([+-]?\d+ mons?\s?)?([+-]?\d+ days?\s?)?(?:(?:([+-])?(\d{2,10}):(\d\d):(\d\d(\.\d+)?))|([+-]?\d+ hours?\s?)?([+-]?\d+ mins?\s?)?([+-]?\d+(\.\d+)? secs?\s?)?)?\z/o
# Parse the interval input string into an ActiveSupport::Duration instance.
def call(string)
raise(InvalidValue, "invalid or unhandled interval format: #{string.inspect}") unless matches = PARSER.match(string)
value = 0
parts = []
if v = matches[1]
v = v.to_i
value += 31557600 * v
parts << [:years, v]
end
if v = matches[2]
v = v.to_i
value += 2592000 * v
parts << [:months, v]
end
if v = matches[3]
v = v.to_i
value += 86400 * v
parts << [:days, v]
end
if matches[5]
seconds = matches[5].to_i * 3600 + matches[6].to_i * 60
seconds += matches[8] ? matches[7].to_f : matches[7].to_i
seconds *= -1 if matches[4] == '-'
value += seconds
parts << [:seconds, seconds]
elsif matches[9] || matches[10] || matches[11]
seconds = 0
if v = matches[9]
seconds += v.to_i * 3600
end
if v = matches[10]
seconds += v.to_i * 60
end
if v = matches[11]
seconds += matches[12] ? v.to_f : v.to_i
end
value += seconds
parts << [:seconds, seconds]
end
ActiveSupport::Duration.new(value, parts)
end
end
# Single instance of Parser used for parsing, to save on memory (since the parser has no state).
PARSER = Parser.new
# Reset the conversion procs if using the native postgres adapter,
# and extend the datasets to correctly literalize ActiveSupport::Duration values.
def self.extended(db)
db.instance_eval do
extend_datasets(IntervalDatasetMethods)
copy_conversion_procs([1186, 1187])
@schema_type_classes[:interval] = ActiveSupport::Duration
end
end
# Handle ActiveSupport::Duration values in bound variables.
def bound_variable_arg(arg, conn)
case arg
when ActiveSupport::Duration
IntervalDatabaseMethods.literal_duration(arg)
else
super
end
end
private
# Handle arrays of interval types in bound variables.
def bound_variable_array(a)
case a
when ActiveSupport::Duration
"\"#{IntervalDatabaseMethods.literal_duration(a)}\""
else
super
end
end
# Typecast value correctly to an ActiveSupport::Duration instance.
# If already an ActiveSupport::Duration, return it.
# If a numeric argument is given, assume it represents a number
# of seconds, and create a new ActiveSupport::Duration instance
# representing that number of seconds.
# If a String, assume it is in PostgreSQL interval output format
# and attempt to parse it.
def typecast_value_interval(value)
case value
when ActiveSupport::Duration
value
when Numeric
ActiveSupport::Duration.new(value, [[:seconds, value]])
when String
PARSER.call(value)
else
raise Sequel::InvalidValue, "invalid value for interval type: #{value.inspect}"
end
end
end
module IntervalDatasetMethods
CAST_INTERVAL = '::interval'.freeze
# Handle literalization of ActiveSupport::Duration objects, treating them as
# PostgreSQL intervals.
def literal_other_append(sql, v)
case v
when ActiveSupport::Duration
literal_append(sql, IntervalDatabaseMethods.literal_duration(v))
sql << CAST_INTERVAL
else
super
end
end
end
PG_TYPES[1186] = Postgres::IntervalDatabaseMethods::PARSER
if defined?(PGArray) && PGArray.respond_to?(:register)
PGArray.register('interval', :oid=>1187, :scalar_oid=>1186)
end
end
Database.register_extension(:pg_interval, Postgres::IntervalDatabaseMethods)
end
|