sequel 3.36.1 → 3.37.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|