File: pg_interval_spec.rb

package info (click to toggle)
ruby-sequel 5.63.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,408 kB
  • sloc: ruby: 113,747; makefile: 3
file content (151 lines) | stat: -rw-r--r-- 8,158 bytes parent folder | download | duplicates (2)
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
require_relative "spec_helper"

begin
  require 'active_support'
  require 'active_support/duration'
  begin
    require 'active_support/gem_version'
  rescue LoadError
  end
  begin
    require 'active_support/version'
  rescue LoadError
  end
rescue LoadError
  warn "Skipping test of pg_interval plugin: can't load active_support/duration"
else
describe "pg_interval extension" do
  before do
    @db = Sequel.connect('mock://postgres')
    @db.extend_datasets{def quote_identifiers?; false end}
    @db.extension(:pg_array, :pg_interval)
  end

  it "should literalize ActiveSupport::Duration instances to strings correctly" do
    @db.literal(ActiveSupport::Duration.new(0, [])).must_equal "'0'::interval"
    @db.literal(ActiveSupport::Duration.new(0, [[:seconds, 0]])).must_equal "'0'::interval"
    @db.literal(ActiveSupport::Duration.new(0, [[:seconds, 10], [:minutes, 20], [:days, 3], [:months, 4], [:years, 6]])).must_equal "'6 years 4 months 3 days 20 minutes 10 seconds '::interval"
    @db.literal(ActiveSupport::Duration.new(0, [[:seconds, 10], [:minutes, 20], [:hours, 8], [:days, 3], [:weeks, 2], [:months, 4], [:years, 6]])).must_equal "'6 years 4 months 2 weeks 3 days 8 hours 20 minutes 10 seconds '::interval"
    @db.literal(ActiveSupport::Duration.new(0, [[:seconds, -10.000001], [:minutes, -20], [:days, -3], [:months, -4], [:years, -6]])).must_equal "'-6 years -4 months -3 days -20 minutes -10.000001 seconds '::interval"
  end

  it "should literalize ActiveSupport::Duration instances with repeated parts correctly" do
    if defined?(ActiveSupport::VERSION::STRING) && ActiveSupport::VERSION::STRING >= '5.1' && ActiveSupport::VERSION::STRING < '6.1'
      @db.literal(ActiveSupport::Duration.new(0, [[:seconds, 2], [:seconds, 1]])).must_equal "'1 seconds '::interval"
      @db.literal(ActiveSupport::Duration.new(0, [[:seconds, 2], [:seconds, 1], [:days, 1], [:days, 4]])).must_equal "'4 days 1 seconds '::interval"
    else
      @db.literal(ActiveSupport::Duration.new(0, [[:seconds, 2], [:seconds, 1]])).must_equal "'3 seconds '::interval"
      @db.literal(ActiveSupport::Duration.new(0, [[:seconds, 2], [:seconds, 1], [:days, 1], [:days, 4]])).must_equal "'5 days 3 seconds '::interval"
    end
  end

  it "should set up conversion procs correctly" do
    cp = @db.conversion_procs
    cp[1186].call("1 sec").must_equal ActiveSupport::Duration.new(1, [[:seconds, 1]])
  end

  it "should set up conversion procs for arrays correctly" do
    cp = @db.conversion_procs
    cp[1187].call("{1 sec}").must_equal [ActiveSupport::Duration.new(1, [[:seconds, 1]])]
  end

  it "should setup conversion proc without array conversion proc if pg_array extension is not loaded" do
    @db = Sequel.connect('mock://postgres')
    @db.extension(:pg_interval, :pg_array)
    cp = @db.conversion_procs
    cp[1186].call("1 sec").must_equal ActiveSupport::Duration.new(1, [[:seconds, 1]])
    cp[1187].must_be_nil
  end

  it "should not affect literalization of custom objects" do
    o = Object.new
    def o.sql_literal(ds) 'v' end
    @db.literal(o).must_equal 'v'
  end

  it "should support using ActiveSupport::Duration instances as bound variables" do
    @db.bound_variable_arg(1, nil).must_equal 1
    @db.bound_variable_arg(ActiveSupport::Duration.new(0, [[:seconds, 0]]), nil).must_equal '0'
    @db.bound_variable_arg(ActiveSupport::Duration.new(0, [[:seconds, -10.000001], [:minutes, -20], [:days, -3], [:months, -4], [:years, -6]]), nil).must_equal '-6 years -4 months -3 days -20 minutes -10.000001 seconds '
  end

  it "should support using ActiveSupport::Duration instances in array types in bound variables" do
    @db.bound_variable_arg(Sequel.pg_array([ActiveSupport::Duration.new(0, [[:seconds, 0]])]), nil).must_equal '{"0"}'
    @db.bound_variable_arg(Sequel.pg_array([ActiveSupport::Duration.new(0, [[:seconds, -10.000001], [:minutes, -20], [:days, -3], [:months, -4], [:years, -6]])]), nil).must_equal '{"-6 years -4 months -3 days -20 minutes -10.000001 seconds "}'
  end

  it "should parse interval type from the schema correctly" do
    @db.fetch = [{:name=>'id', :db_type=>'integer'}, {:name=>'i', :db_type=>'interval'}]
    @db.schema(:items).map{|e| e[1][:type]}.must_equal [:integer, :interval]
  end

  it "should set :ruby_default schema entries if default value is recognized" do
    @db.fetch = [{:name=>'id', :db_type=>'integer', :default=>'1'}, {:name=>'t', :db_type=>'interval', :default=>"'3 days'::interval"}]
    s = @db.schema(:items)
    s[1][1][:ruby_default].must_equal ActiveSupport::Duration.new(3*86400, :days=>3)
  end

  it "should automatically parameterize pg_interval values" do
    @db.extension :pg_auto_parameterize
    v = ActiveSupport::Duration.new(3*86400, :days=>3)
    sql = @db[:table].insert_sql(v)
    sql.must_equal 'INSERT INTO table VALUES ($1::interval)'
    sql.args.length.must_equal 1
    sql.args[0].must_equal v
  end

  it "should automatically parameterize pg_interval values when loading pg_inet after" do
    @db.extension :pg_auto_parameterize, :pg_inet
    v = ActiveSupport::Duration.new(3*86400, :days=>3)
    sql = @db[:table].insert_sql(v)
    sql.must_equal 'INSERT INTO table VALUES ($1::interval)'
    sql.args.length.must_equal 1
    sql.args[0].must_equal v
  end

  it "should support typecasting for the interval type" do
    m = Sequel::Postgres::IntervalDatabaseMethods::Parser
    seconds = m::SECONDS_PER_YEAR + 2*m::SECONDS_PER_MONTH + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7
    parts = {:years => 1, :months => 2, :days => 25, :seconds => 18367}

    if !defined?(ActiveSupport::VERSION::STRING) || ActiveSupport::VERSION::STRING < '5.1'
      parts = parts.to_a
    end

    d = ActiveSupport::Duration.new(seconds, parts)

    @db.typecast_value(:interval, d).object_id.must_equal d.object_id

    @db.typecast_value(:interval, "1 year 2 mons 25 days 05:06:07").is_a?(ActiveSupport::Duration).must_equal true
    @db.typecast_value(:interval, "1 year 2 mons 25 days 05:06:07").must_equal d
    @db.typecast_value(:interval, "1 year 2 mons 25 days -05:06:07").is_a?(ActiveSupport::Duration).must_equal true
    @db.typecast_value(:interval, "1 year 2 mons 25 days -05:06:07").must_equal(d-10*3600-12*60-14)
    @db.typecast_value(:interval, "1 year 2 mons 25 days 05:06:07").parts.sort_by{|k,v| k.to_s}.must_equal d.parts.sort_by{|k,v| k.to_s}
    @db.typecast_value(:interval, "1 year 2 mons 25 days 05:06:07.0").parts.sort_by{|k,v| k.to_s}.must_equal d.parts.sort_by{|k,v| k.to_s}

    @db.typecast_value(:interval, "1 year 2 mons 25 days 5 hours 6 mins").is_a?(ActiveSupport::Duration).must_equal true
    @db.typecast_value(:interval, "1 year 2 mons 25 days 5 hours 6 mins").must_equal(d-7)
    @db.typecast_value(:interval, "1 year 2 mons 25 days 5 hours 6 mins 7 secs").is_a?(ActiveSupport::Duration).must_equal true
    @db.typecast_value(:interval, "1 year 2 mons 25 days 5 hours 6 mins 7 secs").must_equal d
    @db.typecast_value(:interval, "1 year 2 mons 25 days 5 hours 6 mins 7 secs").parts.sort_by{|k,v| k.to_s}.must_equal d.parts.sort_by{|k,v| k.to_s}
    @db.typecast_value(:interval, "1 year 2 mons 25 days 5 hours 6 mins 7.0 secs").parts.sort_by{|k,v| k.to_s}.must_equal d.parts.sort_by{|k,v| k.to_s}

    d2 = ActiveSupport::Duration.new(1, [[:seconds, 1]])
    @db.typecast_value(:interval, 1).is_a?(ActiveSupport::Duration).must_equal true
    @db.typecast_value(:interval, 1).must_equal d2
    @db.typecast_value(:interval, 1).parts.sort_by{|k,v| k.to_s}.must_equal d2.parts.sort_by{|k,v| k.to_s}

    proc{@db.typecast_value(:interval, 'foo')}.must_raise(Sequel::InvalidValue)
    proc{@db.typecast_value(:interval, Object.new)}.must_raise(Sequel::InvalidValue)

    proc{@db.typecast_value(:interval, '1'*1000+' secs')}.must_raise(Sequel::InvalidValue)
    @db.check_string_typecast_bytesize = false
    @db.typecast_value(:interval, '1'*1000+' secs').must_be_kind_of ActiveSupport::Duration
  end

  it "should return correct results for Database#schema_type_class" do
    @db.schema_type_class(:interval).must_equal ActiveSupport::Duration
    @db.schema_type_class(:integer).must_equal Integer
  end
end
end