sequel 3.36.1 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +84 -0
- data/Rakefile +13 -0
- data/bin/sequel +12 -16
- data/doc/advanced_associations.rdoc +36 -67
- data/doc/association_basics.rdoc +11 -16
- data/doc/release_notes/3.37.0.txt +338 -0
- data/doc/schema_modification.rdoc +4 -0
- data/lib/sequel/adapters/jdbc/h2.rb +1 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +26 -8
- data/lib/sequel/adapters/mysql2.rb +4 -3
- data/lib/sequel/adapters/odbc/mssql.rb +2 -2
- data/lib/sequel/adapters/postgres.rb +4 -60
- data/lib/sequel/adapters/shared/mssql.rb +2 -1
- data/lib/sequel/adapters/shared/mysql.rb +0 -5
- data/lib/sequel/adapters/shared/postgres.rb +68 -2
- data/lib/sequel/adapters/shared/sqlite.rb +17 -1
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +12 -1
- data/lib/sequel/adapters/utils/pg_types.rb +76 -0
- data/lib/sequel/core.rb +13 -0
- data/lib/sequel/database/misc.rb +41 -1
- data/lib/sequel/database/schema_generator.rb +23 -10
- data/lib/sequel/database/schema_methods.rb +26 -4
- data/lib/sequel/dataset/graph.rb +2 -1
- data/lib/sequel/dataset/query.rb +62 -2
- data/lib/sequel/extensions/_pretty_table.rb +7 -3
- data/lib/sequel/extensions/arbitrary_servers.rb +5 -4
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +13 -2
- data/lib/sequel/extensions/core_extensions.rb +6 -0
- data/lib/sequel/extensions/eval_inspect.rb +158 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +4 -1
- data/lib/sequel/extensions/named_timezones.rb +4 -0
- data/lib/sequel/extensions/null_dataset.rb +4 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +219 -168
- data/lib/sequel/extensions/pg_array_ops.rb +7 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -4
- data/lib/sequel/extensions/pg_hstore.rb +3 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +7 -2
- data/lib/sequel/extensions/pg_inet.rb +28 -3
- data/lib/sequel/extensions/pg_interval.rb +192 -0
- data/lib/sequel/extensions/pg_json.rb +21 -9
- data/lib/sequel/extensions/pg_range.rb +487 -0
- data/lib/sequel/extensions/pg_range_ops.rb +122 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +3 -2
- data/lib/sequel/extensions/pretty_table.rb +12 -1
- data/lib/sequel/extensions/query.rb +4 -0
- data/lib/sequel/extensions/query_literals.rb +6 -6
- data/lib/sequel/extensions/schema_dumper.rb +39 -38
- data/lib/sequel/extensions/select_remove.rb +4 -0
- data/lib/sequel/extensions/server_block.rb +3 -2
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -0
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +9 -3
- data/lib/sequel/extensions/to_dot.rb +4 -0
- data/lib/sequel/model/associations.rb +150 -91
- data/lib/sequel/plugins/identity_map.rb +2 -2
- data/lib/sequel/plugins/list.rb +1 -0
- data/lib/sequel/plugins/many_through_many.rb +33 -32
- data/lib/sequel/plugins/nested_attributes.rb +11 -3
- data/lib/sequel/plugins/rcte_tree.rb +2 -2
- data/lib/sequel/plugins/schema.rb +1 -1
- data/lib/sequel/sql.rb +14 -14
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/mysql_spec.rb +25 -0
- data/spec/adapters/postgres_spec.rb +572 -28
- data/spec/adapters/sqlite_spec.rb +16 -1
- data/spec/core/database_spec.rb +61 -2
- data/spec/core/dataset_spec.rb +92 -0
- data/spec/core/expression_filters_spec.rb +12 -0
- data/spec/extensions/arbitrary_servers_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +25 -25
- data/spec/extensions/eval_inspect_spec.rb +58 -0
- data/spec/extensions/json_serializer_spec.rb +0 -6
- data/spec/extensions/list_spec.rb +1 -1
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +81 -0
- data/spec/extensions/nested_attributes_spec.rb +21 -4
- data/spec/extensions/pg_array_ops_spec.rb +1 -11
- data/spec/extensions/pg_array_spec.rb +181 -90
- data/spec/extensions/pg_auto_parameterize_spec.rb +3 -3
- data/spec/extensions/pg_hstore_spec.rb +1 -3
- data/spec/extensions/pg_inet_spec.rb +6 -1
- data/spec/extensions/pg_interval_spec.rb +73 -0
- data/spec/extensions/pg_json_spec.rb +5 -9
- data/spec/extensions/pg_range_ops_spec.rb +49 -0
- data/spec/extensions/pg_range_spec.rb +372 -0
- data/spec/extensions/pg_statement_cache_spec.rb +1 -2
- data/spec/extensions/query_literals_spec.rb +1 -2
- data/spec/extensions/schema_dumper_spec.rb +48 -89
- data/spec/extensions/serialization_spec.rb +1 -5
- data/spec/extensions/server_block_spec.rb +2 -2
- data/spec/extensions/spec_helper.rb +12 -2
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/integration/associations_test.rb +4 -4
- data/spec/integration/database_test.rb +2 -2
- data/spec/integration/dataset_test.rb +4 -4
- data/spec/integration/eager_loader_test.rb +6 -6
- data/spec/integration/plugin_test.rb +2 -2
- data/spec/integration/spec_helper.rb +2 -2
- data/spec/model/association_reflection_spec.rb +5 -0
- data/spec/model/associations_spec.rb +156 -49
- data/spec/model/eager_loading_spec.rb +137 -2
- data/spec/model/model_spec.rb +10 -10
- metadata +15 -2
|
@@ -11,7 +11,7 @@ describe "NestedAttributes plugin" do
|
|
|
11
11
|
|
|
12
12
|
def check_sql_array(*shoulds)
|
|
13
13
|
sqls = @db.sqls
|
|
14
|
-
|
|
14
|
+
sqls.length.should == shoulds.length
|
|
15
15
|
shoulds.zip(sqls){|s, i| check_sqls(s, i)}
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -59,9 +59,8 @@ describe "NestedAttributes plugin" do
|
|
|
59
59
|
@db.sqls.should == []
|
|
60
60
|
a.save
|
|
61
61
|
check_sql_array(["INSERT INTO artists (name, id) VALUES ('Ar', 1)", "INSERT INTO artists (id, name) VALUES (1, 'Ar')"],
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
["UPDATE albums SET artist_id = 1, name = 'Al' WHERE (id = 2)", "UPDATE albums SET name = 'Al', artist_id = 1 WHERE (id = 2)"])
|
|
62
|
+
"UPDATE albums SET artist_id = NULL WHERE (artist_id = 1)",
|
|
63
|
+
["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
|
|
65
64
|
end
|
|
66
65
|
|
|
67
66
|
it "should support creating new one_to_many objects" do
|
|
@@ -555,4 +554,22 @@ describe "NestedAttributes plugin" do
|
|
|
555
554
|
proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
|
|
556
555
|
proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
|
|
557
556
|
end
|
|
557
|
+
|
|
558
|
+
it "should accept a proc for the :fields option that accepts the associated object and returns an array of fields" do
|
|
559
|
+
@Tag.columns :id, :name, :number
|
|
560
|
+
@Album.nested_attributes :tags, :destroy=>true, :remove=>true, :fields=>proc{|object| object.is_a?(@Tag) ? [:name] : []}
|
|
561
|
+
|
|
562
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
|
563
|
+
t = @Tag.load(:id=>30, :name=>'T', :number=>10)
|
|
564
|
+
al.associations[:tags] = [t]
|
|
565
|
+
al.set(:tags_attributes=>[{:id=>30, :name=>'T2'}, {:name=>'T3'}])
|
|
566
|
+
@db.sqls.should == []
|
|
567
|
+
al.save
|
|
568
|
+
check_sql_array("UPDATE albums SET name = 'Al' WHERE (id = 10)",
|
|
569
|
+
"UPDATE tags SET name = 'T2' WHERE (id = 30)",
|
|
570
|
+
"INSERT INTO tags (name) VALUES ('T3')",
|
|
571
|
+
["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
|
|
572
|
+
proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
|
|
573
|
+
proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
|
|
574
|
+
end
|
|
558
575
|
end
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
2
|
|
|
3
3
|
describe "Sequel::Postgres::ArrayOp" do
|
|
4
|
-
# Attempt to load pg_array first to test PGArray -> ArrayOp code
|
|
5
|
-
pg_array_loaded = begin
|
|
6
|
-
Sequel.extension :pg_array
|
|
7
|
-
true
|
|
8
|
-
rescue LoadError
|
|
9
|
-
false
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
Sequel.extension :pg_array_ops
|
|
13
|
-
|
|
14
4
|
before do
|
|
15
5
|
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
16
6
|
@a = :a.pg_array
|
|
@@ -101,5 +91,5 @@ describe "Sequel::Postgres::ArrayOp" do
|
|
|
101
91
|
|
|
102
92
|
it "should allow transforming PGArray instances into ArrayOp instances" do
|
|
103
93
|
@db.literal([1,2].pg_array.op.push(3)).should == "(ARRAY[1,2] || 3)"
|
|
104
|
-
end
|
|
94
|
+
end
|
|
105
95
|
end
|
|
@@ -1,108 +1,119 @@
|
|
|
1
1
|
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
2
|
|
|
3
|
-
begin
|
|
4
|
-
Sequel.extension :pg_array
|
|
5
|
-
rescue LoadError => e
|
|
6
|
-
skip_warn "can't load pg_array extension (#{e.class}: #{e})"
|
|
7
|
-
else
|
|
8
3
|
describe "pg_array extension" do
|
|
9
4
|
before do
|
|
10
5
|
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
11
|
-
@db.extend(Module.new{def bound_variable_arg(arg, conn) arg end})
|
|
6
|
+
@db.extend(Module.new{def bound_variable_arg(arg, conn) arg end; def get_conversion_procs(conn) {} end})
|
|
7
|
+
@db.extend_datasets(Module.new{def supports_timestamp_timezones?; false; end; def supports_timestamp_usecs?; false; end})
|
|
8
|
+
@db.extension(:pg_array)
|
|
12
9
|
@m = Sequel::Postgres
|
|
10
|
+
@convertor = @m::PG_TYPES
|
|
13
11
|
end
|
|
14
12
|
|
|
15
13
|
it "should parse single dimensional text arrays" do
|
|
16
|
-
@
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
c = @convertor[1009]
|
|
15
|
+
c.call("{a}").to_a.first.should be_a_kind_of(String)
|
|
16
|
+
c.call("{}").to_a.should == []
|
|
17
|
+
c.call("{a}").to_a.should == ['a']
|
|
18
|
+
c.call('{"a b"}').to_a.should == ['a b']
|
|
19
|
+
c.call('{a,b}').to_a.should == ['a', 'b']
|
|
21
20
|
end
|
|
22
21
|
|
|
23
22
|
it "should parse multi-dimensional text arrays" do
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
c = @convertor[1009]
|
|
24
|
+
c.call("{{}}").to_a.should == [[]]
|
|
25
|
+
c.call("{{a},{b}}").to_a.should == [['a'], ['b']]
|
|
26
|
+
c.call('{{"a b"},{c}}').to_a.should == [['a b'], ['c']]
|
|
27
|
+
c.call('{{{a},{b}},{{c},{d}}}').to_a.should == [[['a'], ['b']], [['c'], ['d']]]
|
|
28
|
+
c.call('{{{a,e},{b,f}},{{c,g},{d,h}}}').to_a.should == [[['a', 'e'], ['b', 'f']], [['c', 'g'], ['d', 'h']]]
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
it "should parse text arrays with embedded deliminaters" do
|
|
32
|
-
|
|
32
|
+
c = @convertor[1009]
|
|
33
|
+
c.call('{{"{},","\\",\\,\\\\\\"\\""}}').to_a.should == [['{},', '",,\\""']]
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
it "should parse single dimensional integer arrays" do
|
|
36
|
-
@
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
c = @convertor[1007]
|
|
38
|
+
c.call("{1}").to_a.first.should be_a_kind_of(Integer)
|
|
39
|
+
c.call("{}").to_a.should == []
|
|
40
|
+
c.call("{1}").to_a.should == [1]
|
|
41
|
+
c.call('{2,3}').to_a.should == [2, 3]
|
|
42
|
+
c.call('{3,4,5}').to_a.should == [3, 4, 5]
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
it "should parse multiple dimensional integer arrays" do
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
c = @convertor[1007]
|
|
47
|
+
c.call("{{}}").to_a.should == [[]]
|
|
48
|
+
c.call("{{1}}").to_a.should == [[1]]
|
|
49
|
+
c.call('{{2},{3}}').to_a.should == [[2], [3]]
|
|
50
|
+
c.call('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
|
48
51
|
end
|
|
49
52
|
|
|
50
53
|
it "should parse single dimensional float arrays" do
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
c = @convertor[1022]
|
|
55
|
+
c.call("{}").to_a.should == []
|
|
56
|
+
c.call("{1.5}").to_a.should == [1.5]
|
|
57
|
+
c.call('{2.5,3.5}').to_a.should == [2.5, 3.5]
|
|
58
|
+
c.call('{3.5,4.5,5.5}').to_a.should == [3.5, 4.5, 5.5]
|
|
55
59
|
end
|
|
56
60
|
|
|
57
61
|
it "should parse multiple dimensional float arrays" do
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
c = @convertor[1022]
|
|
63
|
+
c.call("{{}}").to_a.should == [[]]
|
|
64
|
+
c.call("{{1.5}}").to_a.should == [[1.5]]
|
|
65
|
+
c.call('{{2.5},{3.5}}').to_a.should == [[2.5], [3.5]]
|
|
66
|
+
c.call('{{{1.5,2.5},{3.5,4.5}},{{5.5,6.5},{7.5,8.5}}}').to_a.should == [[[1.5, 2.5], [3.5, 4.5]], [[5.5, 6.5], [7.5, 8.5]]]
|
|
62
67
|
end
|
|
63
68
|
|
|
64
69
|
it "should parse integers in float arrays as floats" do
|
|
65
|
-
@
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
c = @convertor[1022]
|
|
71
|
+
c.call("{1}").to_a.first.should be_a_kind_of(Float)
|
|
72
|
+
c.call("{1}").to_a.should == [1.0]
|
|
73
|
+
c.call('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
it "should parse single dimensional decimal arrays" do
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
c = @convertor[1231]
|
|
78
|
+
c.call("{}").to_a.should == []
|
|
79
|
+
c.call("{1.5}").to_a.should == [BigDecimal.new('1.5')]
|
|
80
|
+
c.call('{2.5,3.5}').to_a.should == [BigDecimal.new('2.5'), BigDecimal.new('3.5')]
|
|
81
|
+
c.call('{3.5,4.5,5.5}').to_a.should == [BigDecimal.new('3.5'), BigDecimal.new('4.5'), BigDecimal.new('5.5')]
|
|
75
82
|
end
|
|
76
83
|
|
|
77
84
|
it "should parse multiple dimensional decimal arrays" do
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
c = @convertor[1231]
|
|
86
|
+
c.call("{{}}").to_a.should == [[]]
|
|
87
|
+
c.call("{{1.5}}").to_a.should == [[BigDecimal.new('1.5')]]
|
|
88
|
+
c.call('{{2.5},{3.5}}').to_a.should == [[BigDecimal.new('2.5')], [BigDecimal.new('3.5')]]
|
|
89
|
+
c.call('{{{1.5,2.5},{3.5,4.5}},{{5.5,6.5},{7.5,8.5}}}').to_a.should == [[[BigDecimal.new('1.5'), BigDecimal.new('2.5')], [BigDecimal.new('3.5'), BigDecimal.new('4.5')]], [[BigDecimal.new('5.5'), BigDecimal.new('6.5')], [BigDecimal.new('7.5'), BigDecimal.new('8.5')]]]
|
|
82
90
|
end
|
|
83
91
|
|
|
84
92
|
it "should parse decimal values with arbitrary precision" do
|
|
85
|
-
|
|
86
|
-
|
|
93
|
+
c = @convertor[1231]
|
|
94
|
+
c.call("{1.000000000000000000005}").to_a.should == [BigDecimal.new('1.000000000000000000005')]
|
|
95
|
+
c.call("{{1.000000000000000000005,2.000000000000000000005},{3.000000000000000000005,4.000000000000000000005}}").to_a.should == [[BigDecimal.new('1.000000000000000000005'), BigDecimal.new('2.000000000000000000005')], [BigDecimal.new('3.000000000000000000005'), BigDecimal.new('4.000000000000000000005')]]
|
|
87
96
|
end
|
|
88
97
|
|
|
89
98
|
it "should parse integers in decimal arrays as BigDecimals" do
|
|
90
|
-
@
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
c = @convertor[1231]
|
|
100
|
+
c.call("{1}").to_a.first.should be_a_kind_of(BigDecimal)
|
|
101
|
+
c.call("{1}").to_a.should == [BigDecimal.new('1')]
|
|
102
|
+
c.call('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[BigDecimal.new('1'), BigDecimal.new('2')], [BigDecimal.new('3'), BigDecimal.new('4')]], [[BigDecimal.new('5'), BigDecimal.new('6')], [BigDecimal.new('7'), BigDecimal.new('8')]]]
|
|
93
103
|
end
|
|
94
104
|
|
|
95
105
|
it "should parse arrays with NULL values" do
|
|
96
|
-
|
|
97
|
-
c.
|
|
98
|
-
c.
|
|
99
|
-
c.
|
|
106
|
+
@convertor.values_at(1007, 1009, 1022, 1231).each do |c|
|
|
107
|
+
c.call("{NULL}").should == [nil]
|
|
108
|
+
c.call("{NULL,NULL}").should == [nil,nil]
|
|
109
|
+
c.call("{{NULL,NULL},{NULL,NULL}}").should == [[nil,nil],[nil,nil]]
|
|
100
110
|
end
|
|
101
111
|
end
|
|
102
112
|
|
|
103
113
|
it 'should parse arrays with "NULL" values' do
|
|
104
|
-
|
|
105
|
-
|
|
114
|
+
c = @convertor[1009]
|
|
115
|
+
c.call('{NULL,"NULL",NULL}').to_a.should == [nil, "NULL", nil]
|
|
116
|
+
c.call('{NULLA,"NULL",NULL}').to_a.should == ["NULLA", "NULL", nil]
|
|
106
117
|
end
|
|
107
118
|
|
|
108
119
|
it "should literalize arrays without types correctly" do
|
|
@@ -135,24 +146,6 @@ describe "pg_array extension" do
|
|
|
135
146
|
@db.literal(@m::PGArray.new([nil, "{},[]'\""], :"varchar(255)")).should == "ARRAY[NULL,'{},[]''\"']::varchar(255)[]"
|
|
136
147
|
end
|
|
137
148
|
|
|
138
|
-
it "should have reasonable default types" do
|
|
139
|
-
@db.literal(@m::PGArray.new([])).should == 'ARRAY[]'
|
|
140
|
-
@db.literal(@m::PGIntegerArray.new([])).should == 'ARRAY[]::int4[]'
|
|
141
|
-
@db.literal(@m::PGFloatArray.new([])).should == 'ARRAY[]::double precision[]'
|
|
142
|
-
@db.literal(@m::PGStringArray.new([])).should == 'ARRAY[]::text[]'
|
|
143
|
-
@db.literal(@m::PGDecimalArray.new([])).should == 'ARRAY[]::decimal[]'
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
it "should use varchar type for char arrays without length" do
|
|
147
|
-
@db.literal(@m::PGStringArray.new([], :char)).should == 'ARRAY[]::varchar[]'
|
|
148
|
-
@db.literal(@m::PGStringArray.new([], 'char')).should == 'ARRAY[]::varchar[]'
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
it "should use given type for char arrays with length" do
|
|
152
|
-
@db.literal(@m::PGStringArray.new([], :'char(2)')).should == 'ARRAY[]::char(2)[]'
|
|
153
|
-
@db.literal(@m::PGStringArray.new([], 'char(1)')).should == 'ARRAY[]::char(1)[]'
|
|
154
|
-
end
|
|
155
|
-
|
|
156
149
|
it "should have Array#pg_array method for easy PGArray creation" do
|
|
157
150
|
@db.literal([1].pg_array).should == 'ARRAY[1]'
|
|
158
151
|
@db.literal([1, 2].pg_array(:int4)).should == 'ARRAY[1,2]::int4[]'
|
|
@@ -160,37 +153,135 @@ describe "pg_array extension" do
|
|
|
160
153
|
end
|
|
161
154
|
|
|
162
155
|
it "should support using arrays as bound variables" do
|
|
163
|
-
@db.extend Sequel::Postgres::PGArray::DatabaseMethods
|
|
164
156
|
@db.bound_variable_arg(1, nil).should == 1
|
|
165
157
|
@db.bound_variable_arg([1,2].pg_array, nil).should == '{1,2}'
|
|
166
158
|
@db.bound_variable_arg([1,2], nil).should == '{1,2}'
|
|
167
159
|
@db.bound_variable_arg([[1,2]], nil).should == '{{1,2}}'
|
|
168
160
|
@db.bound_variable_arg([1.0,2.0], nil).should == '{1.0,2.0}'
|
|
161
|
+
@db.bound_variable_arg([Sequel.lit('a'), Sequel.blob("a\0'\"")], nil).should == '{a,"a\\\\000\\\\047\\""}'
|
|
169
162
|
@db.bound_variable_arg(["\\ \"", 'NULL', nil], nil).should == '{"\\\\ \\"","NULL",NULL}'
|
|
170
163
|
end
|
|
171
164
|
|
|
172
165
|
it "should parse array types from the schema correctly" do
|
|
173
|
-
@db.extend Sequel::Postgres::PGArray::DatabaseMethods
|
|
174
166
|
@db.fetch = [{:name=>'id', :db_type=>'integer'}, {:name=>'i', :db_type=>'integer[]'}, {:name=>'f', :db_type=>'real[]'}, {:name=>'d', :db_type=>'numeric[]'}, {:name=>'t', :db_type=>'text[]'}]
|
|
175
167
|
@db.schema(:items).map{|e| e[1][:type]}.should == [:integer, :integer_array, :float_array, :decimal_array, :string_array]
|
|
176
168
|
end
|
|
177
169
|
|
|
178
170
|
it "should support typecasting of the various array types" do
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
171
|
+
{
|
|
172
|
+
:integer=>{:class=>Integer, :convert=>['1', '1', 1, '1']},
|
|
173
|
+
:float=>{:db_type=>'double precision', :class=>Float, :convert=>['1.1', '1.1', 1.1, '1.1']},
|
|
174
|
+
:decimal=>{:db_type=>'numeric', :class=>BigDecimal, :convert=>['1.00000000000000000000000001', '1.00000000000000000000000001', BigDecimal.new('1.00000000000000000000000001'), '1.00000000000000000000000001']},
|
|
175
|
+
:string=>{:db_type=>'text', :class=>String, :convert=>['1', 1, '1', "'1'"]},
|
|
176
|
+
:bigint=>{:class=>Integer, :convert=>['1', '1', 1, '1']},
|
|
177
|
+
:boolean=>{:class=>TrueClass, :convert=>['t', 't', true, 'true']},
|
|
178
|
+
:blob=>{:db_type=>'bytea', :class=>Sequel::SQL::Blob, :convert=>['1', '1', '1', "'1'"]},
|
|
179
|
+
:date=>{:class=>Date, :convert=>['2011-10-12', '2011-10-12', Date.new(2011, 10, 12), "'2011-10-12'"]},
|
|
180
|
+
:time=>{:db_type=>'time without time zone', :class=>Sequel::SQLTime, :convert=>['01:02:03', '01:02:03', Sequel::SQLTime.create(1, 2, 3), "'01:02:03'"]},
|
|
181
|
+
:datetime=>{:db_type=>'timestamp without time zone', :class=>Time, :convert=>['2011-10-12 01:02:03', '2011-10-12 01:02:03', Time.local(2011, 10, 12, 1, 2, 3), "'2011-10-12 01:02:03'"]},
|
|
182
|
+
:time_timezone=>{:db_type=>'time with time zone', :class=>Sequel::SQLTime, :convert=>['01:02:03', '01:02:03', Sequel::SQLTime.create(1, 2, 3), "'01:02:03'"]},
|
|
183
|
+
:datetime_timezone=>{:db_type=>'timestamp with time zone', :class=>Time, :convert=>['2011-10-12 01:02:03', '2011-10-12 01:02:03', Time.local(2011, 10, 12, 1, 2, 3), "'2011-10-12 01:02:03'"]},
|
|
184
|
+
}.each do |type, h|
|
|
185
|
+
meth = :"#{type}_array"
|
|
186
|
+
db_type = h[:db_type]||type
|
|
187
|
+
klass = h[:class]
|
|
188
|
+
text_in, array_in, value, output = h[:convert]
|
|
189
|
+
|
|
190
|
+
["{#{text_in}}", [array_in]].each do |input|
|
|
191
|
+
v = @db.typecast_value(meth, input)
|
|
192
|
+
v.should == [value]
|
|
193
|
+
v.first.should be_a_kind_of(klass)
|
|
194
|
+
v.array_type.should_not be_nil
|
|
195
|
+
@db.typecast_value(meth, [value].pg_array).should == v
|
|
196
|
+
@db.typecast_value(meth, v).should equal(v)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
["{{#{text_in}}}", [[array_in]]].each do |input|
|
|
200
|
+
v = @db.typecast_value(meth, input)
|
|
201
|
+
v.should == [[value]]
|
|
202
|
+
v.first.first.should be_a_kind_of(klass)
|
|
203
|
+
v.array_type.should_not be_nil
|
|
204
|
+
@db.typecast_value(meth, [[value]].pg_array).should == v
|
|
205
|
+
@db.typecast_value(meth, v).should equal(v)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
@db.literal(@db.typecast_value(meth, [array_in])).should == "ARRAY[#{output}]::#{db_type}[]"
|
|
209
|
+
@db.typecast_value(meth, '{}').should == []
|
|
210
|
+
@db.literal(@db.typecast_value(meth, '{}')).should == "ARRAY[]::#{db_type}[]"
|
|
188
211
|
end
|
|
189
|
-
@db.typecast_value(:integer_array, '{1}').should == [1]
|
|
190
|
-
@db.typecast_value(:float_array, '{1}').should == [1.0]
|
|
191
|
-
@db.typecast_value(:decimal_array, '{1}').should == [BigDecimal.new('1')]
|
|
192
|
-
@db.typecast_value(:string_array, '{1}').should == ['1']
|
|
193
212
|
proc{@db.typecast_value(:integer_array, {})}.should raise_error(Sequel::InvalidValue)
|
|
194
213
|
end
|
|
195
|
-
|
|
214
|
+
|
|
215
|
+
it "should support registering custom array types" do
|
|
216
|
+
Sequel::Postgres::PGArray.register('foo')
|
|
217
|
+
@db.typecast_value(:foo_array, []).should be_a_kind_of(Sequel::Postgres::PGArray)
|
|
218
|
+
@db.fetch = [{:name=>'id', :db_type=>'foo[]'}]
|
|
219
|
+
@db.schema(:items).map{|e| e[1][:type]}.should == [:foo_array]
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it "should support registering custom types with :type_symbol option" do
|
|
223
|
+
Sequel::Postgres::PGArray.register('foo', :type_symbol=>:bar)
|
|
224
|
+
@db.typecast_value(:bar_array, []).should be_a_kind_of(Sequel::Postgres::PGArray)
|
|
225
|
+
@db.fetch = [{:name=>'id', :db_type=>'foo[]'}]
|
|
226
|
+
@db.schema(:items).map{|e| e[1][:type]}.should == [:bar_array]
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it "should support using a block as a custom conversion proc given as block" do
|
|
230
|
+
Sequel::Postgres::PGArray.register('foo'){|s| (s*2).to_i}
|
|
231
|
+
@db.typecast_value(:foo_array, '{1}').should == [11]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it "should support using a block as a custom conversion proc given as :converter option" do
|
|
235
|
+
Sequel::Postgres::PGArray.register('foo', :converter=>proc{|s| (s*2).to_i})
|
|
236
|
+
@db.typecast_value(:foo_array, '{1}').should == [11]
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it "should support using an existing scaler conversion proc via the :scalar_oid option" do
|
|
240
|
+
Sequel::Postgres::PGArray.register('foo', :scalar_oid=>16)
|
|
241
|
+
@db.typecast_value(:foo_array, '{"t"}').should == [true]
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it "should raise an error if using :scalar_oid option with unexisting scalar conversion proc" do
|
|
245
|
+
proc{Sequel::Postgres::PGArray.register('foo', :scalar_oid=>0)}.should raise_error(Sequel::Error)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it "should raise an error if using :converter option and a block argument" do
|
|
249
|
+
proc{Sequel::Postgres::PGArray.register('foo', :converter=>proc{}){}}.should raise_error(Sequel::Error)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it "should raise an error if using :scalar_oid option and a block argument" do
|
|
253
|
+
proc{Sequel::Postgres::PGArray.register('foo', :scalar_oid=>16){}}.should raise_error(Sequel::Error)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it "should support registering custom types with :oid option" do
|
|
257
|
+
Sequel::Postgres::PGArray.register('foo', :oid=>1)
|
|
258
|
+
Sequel::Postgres::PG_TYPES[1].call('{1}').should be_a_kind_of(Sequel::Postgres::PGArray)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
it "should support registering custom types with :parser=>:json option" do
|
|
262
|
+
Sequel::Postgres::PGArray.register('foo', :oid=>2, :parser=>:json)
|
|
263
|
+
Sequel::Postgres::PG_TYPES[2].should be_a_kind_of(Sequel::Postgres::PGArray::JSONCreator)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it "should support registering convertors with :parser=>:json option" do
|
|
267
|
+
Sequel::Postgres::PGArray.register('foo', :oid=>4, :parser=>:json){|s| s * 2}
|
|
268
|
+
Sequel::Postgres::PG_TYPES[4].call('{{1, 2}, {3, 4}}').should == [[2, 4], [6, 8]]
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
it "should support registering custom types with :array_type option" do
|
|
272
|
+
Sequel::Postgres::PGArray.register('foo', :oid=>3, :array_type=>:blah)
|
|
273
|
+
@db.literal(Sequel::Postgres::PG_TYPES[3].call('{}')).should == 'ARRAY[]::blah[]'
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it "should use and not override existing database typecast method if :typecast_method option is given" do
|
|
277
|
+
Sequel::Postgres::PGArray.register('foo', :typecast_method=>:float)
|
|
278
|
+
@db.fetch = [{:name=>'id', :db_type=>'foo[]'}]
|
|
279
|
+
@db.schema(:items).map{|e| e[1][:type]}.should == [:float_array]
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it "should set appropriate timestamp conversion procs when getting conversion procs" do
|
|
283
|
+
procs = @db.send(:get_conversion_procs, nil)
|
|
284
|
+
procs[1185].call('{"2011-10-20 11:12:13"}').should == [Time.local(2011, 10, 20, 11, 12, 13)]
|
|
285
|
+
procs[1115].call('{"2011-10-20 11:12:13"}').should == [Time.local(2011, 10, 20, 11, 12, 13)]
|
|
286
|
+
end
|
|
196
287
|
end
|
|
@@ -5,7 +5,7 @@ describe "pg_auto_parameterize extension" do
|
|
|
5
5
|
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
6
6
|
@db.synchronize{|c| def c.escape_bytea(v) v*2 end}
|
|
7
7
|
@db.extend_datasets{def use_cursor(*) clone end}
|
|
8
|
-
@db.
|
|
8
|
+
@db.extension :pg_auto_parameterize
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
it "should automatically parameterize queries strings, blobs, numerics, dates, and times" do
|
|
@@ -20,7 +20,7 @@ describe "pg_auto_parameterize extension" do
|
|
|
20
20
|
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::int8)', 18446744073709551616)
|
|
21
21
|
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::double precision)', 1.1)
|
|
22
22
|
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::numeric)', BigDecimal.new('1.01'))
|
|
23
|
-
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1
|
|
23
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1)', "a")
|
|
24
24
|
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::bytea)', "a\0b".to_sequel_blob)
|
|
25
25
|
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = 1)', '1'.lit, :nil)
|
|
26
26
|
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::time)', Sequel::SQLTime.create(1, 2, 3, 500000))
|
|
@@ -38,7 +38,7 @@ describe "pg_auto_parameterize extension" do
|
|
|
38
38
|
@db.sqls.should == ['SELECT * FROM table WHERE (a = $1::int4) -- args: [1]']
|
|
39
39
|
|
|
40
40
|
@db[:table].filter(:a=>1).update(:b=>'a').should == 1
|
|
41
|
-
@db.sqls.should == ['UPDATE table SET b = $1
|
|
41
|
+
@db.sqls.should == ['UPDATE table SET b = $1 WHERE (a = $2::int4) -- args: ["a", 1]']
|
|
42
42
|
|
|
43
43
|
@db[:table].filter(:a=>1).delete.should == 1
|
|
44
44
|
@db.sqls.should == ['DELETE FROM table WHERE (a = $1::int4) -- args: [1]']
|
|
@@ -6,6 +6,7 @@ describe "pg_hstore extension" do
|
|
|
6
6
|
@db.extend(Module.new{def bound_variable_arg(arg, conn) arg end})
|
|
7
7
|
@m = Sequel::Postgres
|
|
8
8
|
@c = @m::HStore
|
|
9
|
+
@db.extension :pg_hstore
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
it "should parse hstore strings correctly" do
|
|
@@ -164,7 +165,6 @@ describe "pg_hstore extension" do
|
|
|
164
165
|
end
|
|
165
166
|
|
|
166
167
|
it "should support using hstores as bound variables" do
|
|
167
|
-
@db.extend @c::DatabaseMethods
|
|
168
168
|
@db.bound_variable_arg(1, nil).should == 1
|
|
169
169
|
@db.bound_variable_arg({'1'=>'2'}, nil).should == '"1"=>"2"'
|
|
170
170
|
@db.bound_variable_arg({'1'=>'2'}.hstore, nil).should == '"1"=>"2"'
|
|
@@ -175,13 +175,11 @@ describe "pg_hstore extension" do
|
|
|
175
175
|
end
|
|
176
176
|
|
|
177
177
|
it "should parse hstore type from the schema correctly" do
|
|
178
|
-
@db.extend @c::DatabaseMethods
|
|
179
178
|
@db.fetch = [{:name=>'id', :db_type=>'integer'}, {:name=>'i', :db_type=>'hstore'}]
|
|
180
179
|
@db.schema(:items).map{|e| e[1][:type]}.should == [:integer, :hstore]
|
|
181
180
|
end
|
|
182
181
|
|
|
183
182
|
it "should support typecasting for the hstore type" do
|
|
184
|
-
@db.extend @c::DatabaseMethods
|
|
185
183
|
h = {1=>2}.hstore
|
|
186
184
|
@db.typecast_value(:hstore, h).should equal(h)
|
|
187
185
|
@db.typecast_value(:hstore, '').should be_a_kind_of(@c)
|