sequel 3.37.0 → 3.38.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 +56 -0
- data/README.rdoc +82 -58
- data/Rakefile +6 -5
- data/bin/sequel +1 -1
- data/doc/active_record.rdoc +67 -52
- data/doc/advanced_associations.rdoc +33 -48
- data/doc/association_basics.rdoc +41 -51
- data/doc/cheat_sheet.rdoc +21 -21
- data/doc/core_extensions.rdoc +374 -0
- data/doc/dataset_basics.rdoc +5 -5
- data/doc/dataset_filtering.rdoc +47 -43
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +4 -5
- data/doc/model_hooks.rdoc +3 -3
- data/doc/object_model.rdoc +31 -25
- data/doc/opening_databases.rdoc +19 -5
- data/doc/prepared_statements.rdoc +2 -2
- data/doc/querying.rdoc +109 -52
- data/doc/reflection.rdoc +6 -6
- data/doc/release_notes/3.38.0.txt +234 -0
- data/doc/schema_modification.rdoc +22 -13
- data/doc/sharding.rdoc +8 -9
- data/doc/sql.rdoc +154 -112
- data/doc/testing.rdoc +47 -7
- data/doc/thread_safety.rdoc +1 -1
- data/doc/transactions.rdoc +1 -1
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +29 -43
- data/lib/sequel/adapters/do/postgres.rb +1 -4
- data/lib/sequel/adapters/jdbc.rb +14 -3
- data/lib/sequel/adapters/jdbc/db2.rb +9 -0
- data/lib/sequel/adapters/jdbc/derby.rb +41 -4
- data/lib/sequel/adapters/jdbc/jtds.rb +11 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -6
- data/lib/sequel/adapters/mock.rb +10 -4
- data/lib/sequel/adapters/postgres.rb +1 -28
- data/lib/sequel/adapters/shared/mssql.rb +23 -13
- data/lib/sequel/adapters/shared/postgres.rb +46 -0
- data/lib/sequel/adapters/swift.rb +21 -13
- data/lib/sequel/adapters/swift/mysql.rb +1 -0
- data/lib/sequel/adapters/swift/postgres.rb +4 -5
- data/lib/sequel/adapters/swift/sqlite.rb +2 -1
- data/lib/sequel/adapters/tinytds.rb +14 -2
- data/lib/sequel/adapters/utils/pg_types.rb +5 -0
- data/lib/sequel/core.rb +29 -17
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +3 -0
- data/lib/sequel/dataset/actions.rb +5 -6
- data/lib/sequel/dataset/query.rb +7 -7
- data/lib/sequel/dataset/sql.rb +5 -18
- data/lib/sequel/extensions/core_extensions.rb +8 -12
- data/lib/sequel/extensions/pg_array.rb +59 -33
- data/lib/sequel/extensions/pg_array_ops.rb +32 -4
- data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
- data/lib/sequel/extensions/pg_hstore.rb +32 -17
- data/lib/sequel/extensions/pg_hstore_ops.rb +32 -3
- data/lib/sequel/extensions/pg_inet.rb +1 -2
- data/lib/sequel/extensions/pg_interval.rb +0 -1
- data/lib/sequel/extensions/pg_json.rb +41 -23
- data/lib/sequel/extensions/pg_range.rb +36 -11
- data/lib/sequel/extensions/pg_range_ops.rb +32 -4
- data/lib/sequel/extensions/pg_row.rb +572 -0
- data/lib/sequel/extensions/pg_row_ops.rb +164 -0
- data/lib/sequel/extensions/query.rb +3 -3
- data/lib/sequel/extensions/schema_dumper.rb +7 -8
- data/lib/sequel/extensions/select_remove.rb +1 -1
- data/lib/sequel/model/base.rb +1 -0
- data/lib/sequel/no_core_ext.rb +1 -1
- data/lib/sequel/plugins/pg_row.rb +121 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
- data/lib/sequel/plugins/validation_helpers.rb +31 -0
- data/lib/sequel/sql.rb +64 -44
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +37 -12
- data/spec/adapters/mysql_spec.rb +39 -75
- data/spec/adapters/oracle_spec.rb +11 -11
- data/spec/adapters/postgres_spec.rb +414 -237
- data/spec/adapters/spec_helper.rb +1 -1
- data/spec/adapters/sqlite_spec.rb +14 -14
- data/spec/core/database_spec.rb +6 -6
- data/spec/core/dataset_spec.rb +169 -205
- data/spec/core/expression_filters_spec.rb +182 -295
- data/spec/core/object_graph_spec.rb +6 -6
- data/spec/core/schema_spec.rb +14 -14
- data/spec/core/spec_helper.rb +1 -0
- data/spec/{extensions/core_extensions_spec.rb → core_extensions_spec.rb} +208 -14
- data/spec/extensions/columns_introspection_spec.rb +5 -5
- data/spec/extensions/hook_class_methods_spec.rb +28 -36
- data/spec/extensions/many_through_many_spec.rb +4 -4
- data/spec/extensions/pg_array_ops_spec.rb +15 -7
- data/spec/extensions/pg_array_spec.rb +81 -48
- data/spec/extensions/pg_auto_parameterize_spec.rb +2 -2
- data/spec/extensions/pg_hstore_ops_spec.rb +13 -9
- data/spec/extensions/pg_hstore_spec.rb +66 -65
- data/spec/extensions/pg_inet_spec.rb +2 -4
- data/spec/extensions/pg_interval_spec.rb +2 -3
- data/spec/extensions/pg_json_spec.rb +20 -18
- data/spec/extensions/pg_range_ops_spec.rb +11 -4
- data/spec/extensions/pg_range_spec.rb +30 -7
- data/spec/extensions/pg_row_ops_spec.rb +48 -0
- data/spec/extensions/pg_row_plugin_spec.rb +45 -0
- data/spec/extensions/pg_row_spec.rb +323 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
- data/spec/extensions/query_literals_spec.rb +11 -11
- data/spec/extensions/query_spec.rb +3 -3
- data/spec/extensions/schema_dumper_spec.rb +20 -4
- data/spec/extensions/schema_spec.rb +18 -41
- data/spec/extensions/select_remove_spec.rb +4 -4
- data/spec/extensions/spec_helper.rb +4 -8
- data/spec/extensions/to_dot_spec.rb +5 -5
- data/spec/extensions/validation_class_methods_spec.rb +28 -16
- data/spec/integration/associations_test.rb +20 -20
- data/spec/integration/dataset_test.rb +98 -98
- data/spec/integration/eager_loader_test.rb +13 -27
- data/spec/integration/plugin_test.rb +5 -5
- data/spec/integration/prepared_statement_test.rb +22 -13
- data/spec/integration/schema_test.rb +28 -18
- data/spec/integration/spec_helper.rb +1 -1
- data/spec/integration/timezone_test.rb +2 -2
- data/spec/integration/type_test.rb +15 -6
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +4 -4
- data/spec/model/base_spec.rb +5 -5
- data/spec/model/eager_loading_spec.rb +15 -15
- data/spec/model/model_spec.rb +32 -32
- data/spec/model/record_spec.rb +16 -0
- data/spec/model/spec_helper.rb +2 -6
- data/spec/model/validations_spec.rb +1 -1
- metadata +16 -4
|
@@ -4,9 +4,7 @@ describe "pg_inet extension" do
|
|
|
4
4
|
ipv6_broken = (IPAddr.new('::1'); false) rescue true
|
|
5
5
|
before do
|
|
6
6
|
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
7
|
-
@db.
|
|
8
|
-
@db.extension(:pg_array)
|
|
9
|
-
@db.extension(:pg_inet)
|
|
7
|
+
@db.extension(:pg_array, :pg_inet)
|
|
10
8
|
end
|
|
11
9
|
|
|
12
10
|
it "should literalize IPAddr v4 instances to strings correctly" do
|
|
@@ -31,7 +29,7 @@ describe "pg_inet extension" do
|
|
|
31
29
|
end
|
|
32
30
|
|
|
33
31
|
it "should support using IPAddr instances in array types in bound variables" do
|
|
34
|
-
@db.bound_variable_arg([IPAddr.new('127.0.0.1')]
|
|
32
|
+
@db.bound_variable_arg(Sequel.pg_array([IPAddr.new('127.0.0.1')]), nil).should == '{"127.0.0.1/32"}'
|
|
35
33
|
end
|
|
36
34
|
|
|
37
35
|
it "should parse inet/cidr type from the schema correctly" do
|
|
@@ -8,7 +8,6 @@ else
|
|
|
8
8
|
describe "pg_interval extension" do
|
|
9
9
|
before do
|
|
10
10
|
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
11
|
-
@db.extend(Module.new{def bound_variable_arg(arg, conn) arg end})
|
|
12
11
|
@db.extension(:pg_array, :pg_interval)
|
|
13
12
|
end
|
|
14
13
|
|
|
@@ -37,8 +36,8 @@ describe "pg_interval extension" do
|
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
it "should support using ActiveSupport::Duration instances in array types in bound variables" do
|
|
40
|
-
@db.bound_variable_arg([ActiveSupport::Duration.new(0, [[:seconds, 0]])]
|
|
41
|
-
@db.bound_variable_arg([ActiveSupport::Duration.new(0, [[:seconds, -10.000001], [:minutes, -20], [:days, -3], [:months, -4], [:years, -6]])]
|
|
39
|
+
@db.bound_variable_arg(Sequel.pg_array([ActiveSupport::Duration.new(0, [[:seconds, 0]])]), nil).should == '{"0"}'
|
|
40
|
+
@db.bound_variable_arg(Sequel.pg_array([ActiveSupport::Duration.new(0, [[:seconds, -10.000001], [:minutes, -20], [:days, -3], [:months, -4], [:years, -6]])]), nil).should == '{"-6 years -4 months -3 days -20 minutes -10.000001 seconds "}'
|
|
42
41
|
end
|
|
43
42
|
|
|
44
43
|
it "should parse interval type from the schema correctly" do
|
|
@@ -20,7 +20,6 @@ describe "pg_json extension" do
|
|
|
20
20
|
end
|
|
21
21
|
before do
|
|
22
22
|
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
23
|
-
@db.extend(Module.new{def bound_variable_arg(arg, conn) arg end})
|
|
24
23
|
@db.extension(:pg_array, :pg_json)
|
|
25
24
|
end
|
|
26
25
|
|
|
@@ -43,36 +42,39 @@ describe "pg_json extension" do
|
|
|
43
42
|
end
|
|
44
43
|
|
|
45
44
|
it "should literalize HStores to strings correctly" do
|
|
46
|
-
@db.literal([]
|
|
47
|
-
@db.literal([1, [2], {'a'=>'b'}]
|
|
48
|
-
@db.literal({}
|
|
49
|
-
@db.literal(
|
|
45
|
+
@db.literal(Sequel.pg_json([])).should == "'[]'::json"
|
|
46
|
+
@db.literal(Sequel.pg_json([1, [2], {'a'=>'b'}])).should == "'[1,[2],{\"a\":\"b\"}]'::json"
|
|
47
|
+
@db.literal(Sequel.pg_json({})).should == "'{}'::json"
|
|
48
|
+
@db.literal(Sequel.pg_json('a'=>'b')).should == "'{\"a\":\"b\"}'::json"
|
|
50
49
|
end
|
|
51
50
|
|
|
52
|
-
it "should have
|
|
53
|
-
|
|
51
|
+
it "should have Sequel.pg_json return JSONHash and JSONArray as is" do
|
|
52
|
+
a = Sequel.pg_json({})
|
|
53
|
+
Sequel.pg_json(a).should equal(a)
|
|
54
|
+
a = Sequel.pg_json([])
|
|
55
|
+
Sequel.pg_json(a).should equal(a)
|
|
54
56
|
end
|
|
55
57
|
|
|
56
|
-
it "should have
|
|
57
|
-
|
|
58
|
+
it "should have Sequel.pg_json raise an Error if called with a non-hash or array" do
|
|
59
|
+
proc{Sequel.pg_json(:a)}.should raise_error(Sequel::Error)
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
it "should have JSONHash#to_hash method for getting underlying hash" do
|
|
61
|
-
{}.
|
|
63
|
+
Sequel.pg_json({}).to_hash.should be_a_kind_of(Hash)
|
|
62
64
|
end
|
|
63
65
|
|
|
64
66
|
it "should have JSONArray#to_a method for getting underlying array" do
|
|
65
|
-
[].
|
|
67
|
+
Sequel.pg_json([]).to_a.should be_a_kind_of(Array)
|
|
66
68
|
end
|
|
67
69
|
|
|
68
70
|
it "should support using JSONHash and JSONArray as bound variables" do
|
|
69
71
|
@db.bound_variable_arg(1, nil).should == 1
|
|
70
|
-
@db.bound_variable_arg([1]
|
|
71
|
-
@db.bound_variable_arg(
|
|
72
|
+
@db.bound_variable_arg(Sequel.pg_json([1]), nil).should == '[1]'
|
|
73
|
+
@db.bound_variable_arg(Sequel.pg_json('a'=>'b'), nil).should == '{"a":"b"}'
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
it "should support using json[] types in bound variables" do
|
|
75
|
-
@db.bound_variable_arg([[{"a"=>1}].pg_json
|
|
77
|
+
@db.bound_variable_arg(Sequel.pg_array([Sequel.pg_json([{"a"=>1}]), Sequel.pg_json("b"=>[1, 2])]), nil).should == '{"[{\\"a\\":1}]","{\\"b\\":[1,2]}"}'
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
it "should parse json type from the schema correctly" do
|
|
@@ -81,16 +83,16 @@ describe "pg_json extension" do
|
|
|
81
83
|
end
|
|
82
84
|
|
|
83
85
|
it "should support typecasting for the json type" do
|
|
84
|
-
h =
|
|
85
|
-
a = [1]
|
|
86
|
+
h = Sequel.pg_json(1=>2)
|
|
87
|
+
a = Sequel.pg_json([1])
|
|
86
88
|
@db.typecast_value(:json, h).should equal(h)
|
|
87
89
|
@db.typecast_value(:json, h.to_hash).should == h
|
|
88
90
|
@db.typecast_value(:json, h.to_hash).should be_a_kind_of(@hc)
|
|
89
91
|
@db.typecast_value(:json, a).should equal(a)
|
|
90
92
|
@db.typecast_value(:json, a.to_a).should == a
|
|
91
93
|
@db.typecast_value(:json, a.to_a).should be_a_kind_of(@ac)
|
|
92
|
-
@db.typecast_value(:json, '[]').should == []
|
|
93
|
-
@db.typecast_value(:json, '{"a": "b"}').should ==
|
|
94
|
+
@db.typecast_value(:json, '[]').should == Sequel.pg_json([])
|
|
95
|
+
@db.typecast_value(:json, '{"a": "b"}').should == Sequel.pg_json("a"=>"b")
|
|
94
96
|
proc{@db.typecast_value(:json, '')}.should raise_error(Sequel::InvalidValue)
|
|
95
97
|
proc{@db.typecast_value(:json, 1)}.should raise_error(Sequel::InvalidValue)
|
|
96
98
|
end
|
|
@@ -3,21 +3,28 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
|
3
3
|
describe "Sequel::Postgres::RangeOp" do
|
|
4
4
|
before do
|
|
5
5
|
@ds = Sequel.connect('mock://postgres', :quote_identifiers=>false).dataset
|
|
6
|
-
@h = :h
|
|
6
|
+
@h = Sequel.pg_range_op(:h)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
it "#pg_range should return self" do
|
|
10
10
|
@h.pg_range.should equal(@h)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
it "
|
|
14
|
-
|
|
13
|
+
it "Sequel.pg_range_op should return argument if already a RangeOp" do
|
|
14
|
+
Sequel.pg_range_op(@h).should equal(@h)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "Sequel.pg_range should return a new RangeOp if not given a range" do
|
|
18
|
+
@ds.literal(Sequel.pg_range(:h).lower).should == "lower(h)"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "#pg_range should return a RangeOp for literal strings, and expressions" do
|
|
15
22
|
@ds.literal(Sequel.function(:b, :h).pg_range.lower).should == "lower(b(h))"
|
|
16
23
|
@ds.literal(Sequel.lit('h').pg_range.lower).should == "lower(h)"
|
|
17
24
|
end
|
|
18
25
|
|
|
19
26
|
it "PGRange#op should return a RangeOp" do
|
|
20
|
-
@ds.literal((1..2
|
|
27
|
+
@ds.literal(Sequel.pg_range(1..2, :numrange).op.lower).should == "lower('[1,2]'::numrange)"
|
|
21
28
|
end
|
|
22
29
|
|
|
23
30
|
it "should define methods for all of the the PostgreSQL range operators" do
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
2
|
|
|
3
3
|
describe "pg_range extension" do
|
|
4
|
+
before(:all) do
|
|
5
|
+
@pg_types = Sequel::Postgres::PG_TYPES.dup
|
|
6
|
+
end
|
|
7
|
+
after(:all) do
|
|
8
|
+
Sequel::Postgres::PG_TYPES.replace(@pg_types)
|
|
9
|
+
end
|
|
10
|
+
|
|
4
11
|
before do
|
|
5
12
|
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
6
13
|
@R = Sequel::Postgres::PGRange
|
|
7
|
-
@db.extend(Module.new{def get_conversion_procs(conn) {} end; def bound_variable_arg(arg, conn) arg end})
|
|
8
14
|
@db.extend_datasets(Module.new{def supports_timestamp_timezones?; false; end; def supports_timestamp_usecs?; false; end})
|
|
9
|
-
@db.extension(:pg_array)
|
|
10
|
-
@db.extension(:pg_range)
|
|
15
|
+
@db.extension(:pg_array, :pg_range)
|
|
11
16
|
end
|
|
12
17
|
|
|
13
18
|
it "should literalize Range instances to strings correctly" do
|
|
@@ -204,13 +209,13 @@ describe "pg_range extension" do
|
|
|
204
209
|
end
|
|
205
210
|
|
|
206
211
|
it "should set appropriate timestamp range conversion procs when getting conversion procs" do
|
|
207
|
-
procs = @db.
|
|
212
|
+
procs = @db.conversion_procs
|
|
208
213
|
procs[3908].call('[2011-10-20 11:12:13,2011-10-20 11:12:14]').should == (Time.local(2011, 10, 20, 11, 12, 13)..(Time.local(2011, 10, 20, 11, 12, 14)))
|
|
209
214
|
procs[3910].call('[2011-10-20 11:12:13,2011-10-20 11:12:14]').should == (Time.local(2011, 10, 20, 11, 12, 13)..(Time.local(2011, 10, 20, 11, 12, 14)))
|
|
210
215
|
end
|
|
211
216
|
|
|
212
217
|
it "should set appropriate timestamp range array conversion procs when getting conversion procs" do
|
|
213
|
-
procs = @db.
|
|
218
|
+
procs = @db.conversion_procs
|
|
214
219
|
procs[3909].call('{"[2011-10-20 11:12:13,2011-10-20 11:12:14]"}').should == [Time.local(2011, 10, 20, 11, 12, 13)..Time.local(2011, 10, 20, 11, 12, 14)]
|
|
215
220
|
procs[3911].call('{"[2011-10-20 11:12:13,2011-10-20 11:12:14]"}').should == [Time.local(2011, 10, 20, 11, 12, 13)..Time.local(2011, 10, 20, 11, 12, 14)]
|
|
216
221
|
end
|
|
@@ -240,8 +245,26 @@ describe "pg_range extension" do
|
|
|
240
245
|
@r3.db_type.should == 'int8range'
|
|
241
246
|
end
|
|
242
247
|
|
|
243
|
-
it "should be able to be created by
|
|
244
|
-
(1..2).
|
|
248
|
+
it "should be able to be created by Sequel.pg_range" do
|
|
249
|
+
Sequel.pg_range(1..2).should == @r1
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it "should have Sequel.pg_range be able to take a database type" do
|
|
253
|
+
Sequel.pg_range(1..2, :int4range).should == @R.new(1, 2, :db_type=>:int4range)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it "should have Sequel.pg_range return a PGRange as is" do
|
|
257
|
+
a = Sequel.pg_range(1..2)
|
|
258
|
+
Sequel.pg_range(a).should equal(a)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
it "should have Sequel.pg_range return a new PGRange if the database type differs" do
|
|
262
|
+
a = Sequel.pg_range(1..2, :int4range)
|
|
263
|
+
b = Sequel.pg_range(a, :int8range)
|
|
264
|
+
a.to_range.should == b.to_range
|
|
265
|
+
a.should_not equal(b)
|
|
266
|
+
a.db_type.should == :int4range
|
|
267
|
+
b.db_type.should == :int8range
|
|
245
268
|
end
|
|
246
269
|
|
|
247
270
|
it "should have #initialize raise if requesting an empty range with beginning or ending" do
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "Sequel::Postgres::PGRowOp" do
|
|
4
|
+
before do
|
|
5
|
+
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
6
|
+
@a = Sequel.pg_row_op(:a)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "#[] should access members of the composite type" do
|
|
10
|
+
@db.literal(@a[:b]).should == "(a).b"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "#[] should be chainable" do
|
|
14
|
+
@db.literal(@a[:b][:c]).should == "((a).b).c"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "#[] should support array access if not given an identifier" do
|
|
18
|
+
@db.literal(@a[:b][1]).should == "(a).b[1]"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "#[] should be chainable with array access" do
|
|
22
|
+
@db.literal(@a[1][:b]).should == "(a[1]).b"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "#splat should return a splatted argument" do
|
|
26
|
+
@db.literal(@a.splat).should == "(a.*)"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "#splat(type) should return a splatted argument cast to given type" do
|
|
30
|
+
@db.literal(@a.splat(:b)).should == "(a.*)::b"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "#splat should not work on an already accessed composite type" do
|
|
34
|
+
proc{@a[:a].splat(:b)}.should raise_error(Sequel::Error)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "#pg_row should be callable on literal strings" do
|
|
38
|
+
@db.literal(Sequel.lit('a').pg_row[:b]).should == "(a).b"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "#pg_row should be callable on Sequel expressions" do
|
|
42
|
+
@db.literal(Sequel.function(:a).pg_row[:b]).should == "(a()).b"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "Sequel.pg_row should work as well if the pg_row extension is loaded" do
|
|
46
|
+
@db.literal(Sequel.pg_row(Sequel.function(:a))[:b]).should == "(a()).b"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "Sequel::Plugins::PgRow" do
|
|
4
|
+
before(:all) do
|
|
5
|
+
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
6
|
+
@db.extension(:pg_array)
|
|
7
|
+
@c = Class.new(Sequel::Model(@db[:address]))
|
|
8
|
+
@c.columns :street, :city
|
|
9
|
+
@c.db_schema[:street][:type] = :string
|
|
10
|
+
@c.db_schema[:city][:type] = :string
|
|
11
|
+
@db.fetch = [[{:oid=>1098, :typrelid=>2, :typarray=>3}], [{:attname=>'street', :atttypid=>1324}, {:attname=>'city', :atttypid=>1324}]]
|
|
12
|
+
@c.plugin :pg_row
|
|
13
|
+
|
|
14
|
+
@c2 = Class.new(Sequel::Model(@db[:company]))
|
|
15
|
+
@c2.columns :address
|
|
16
|
+
@c2.db_schema[:address].merge!(:type=>:pg_row_address)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should set up a parser for the type that creates a model class" do
|
|
20
|
+
@db.conversion_procs[1098].call('(123 Foo St,Bar City)').should == @c.load(:street=>'123 Foo St', :city=>'Bar City')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should set up type casting for the type" do
|
|
24
|
+
@c2.new(:address=>{'street'=>123, 'city'=>:Bar}).address.should == @c.load(:street=>'123', :city=>'Bar')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should return model instances as is when typecasting to rows" do
|
|
28
|
+
o = @c.load(:street=>'123', :city=>'Bar')
|
|
29
|
+
@c2.new(:address=>o).address.should equal(o)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should handle literalizing model instances" do
|
|
33
|
+
@db.literal(@c.load(:street=>'123 Foo St', :city=>'Bar City')).should == "ROW('123 Foo St', 'Bar City')::address"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should handle model instances in bound variables" do
|
|
37
|
+
@db.bound_variable_arg(1, nil).should == 1
|
|
38
|
+
@db.bound_variable_arg(@c.load(:street=>'123 Foo St', :city=>'Bar City'), nil).should == '("123 Foo St","Bar City")'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should handle model instances in arrays of bound variables" do
|
|
42
|
+
@db.bound_variable_arg(1, nil).should == 1
|
|
43
|
+
@db.bound_variable_arg(Sequel.pg_array([@c.load(:street=>'123 Foo St', :city=>'Bar City')]), nil).should == '{"(\\"123 Foo St\\",\\"Bar City\\")"}'
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "pg_row extension" do
|
|
4
|
+
before do
|
|
5
|
+
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
6
|
+
@db.extension(:pg_array, :pg_row)
|
|
7
|
+
@m = Sequel::Postgres::PGRow
|
|
8
|
+
@db.sqls
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "should parse record objects as arrays" do
|
|
12
|
+
a = Sequel::Postgres::PG_TYPES[2249].call("(a,b,c)")
|
|
13
|
+
a.should be_a_kind_of(@m::ArrayRow)
|
|
14
|
+
a.to_a.should be_a_kind_of(Array)
|
|
15
|
+
a[0].should == 'a'
|
|
16
|
+
a.should == %w'a b c'
|
|
17
|
+
a.db_type.should == nil
|
|
18
|
+
@db.literal(a).should == "ROW('a', 'b', 'c')"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should parse arrays of record objects as arrays of arrays" do
|
|
22
|
+
as = Sequel::Postgres::PG_TYPES[2287].call('{"(a,b,c)","(d,e,f)"}')
|
|
23
|
+
as.should == [%w'a b c', %w'd e f']
|
|
24
|
+
as.each do |a|
|
|
25
|
+
a.should be_a_kind_of(@m::ArrayRow)
|
|
26
|
+
a.to_a.should be_a_kind_of(Array)
|
|
27
|
+
a.db_type.should == nil
|
|
28
|
+
end
|
|
29
|
+
@db.literal(as).should == "ARRAY[ROW('a', 'b', 'c'),ROW('d', 'e', 'f')]::record[]"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should be able to register custom parsing of row types as array-like objects" do
|
|
33
|
+
klass = @m::ArrayRow.subclass(:foo)
|
|
34
|
+
parser = @m::Parser.new(:converter=>klass)
|
|
35
|
+
a = parser.call("(a,b,c)")
|
|
36
|
+
a.should be_a_kind_of(klass)
|
|
37
|
+
a.to_a.should be_a_kind_of(Array)
|
|
38
|
+
a[0].should == 'a'
|
|
39
|
+
a.should == %w'a b c'
|
|
40
|
+
a.db_type.should == :foo
|
|
41
|
+
@db.literal(a).should == "ROW('a', 'b', 'c')::foo"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should be able to register custom parsing of row types as hash-like objects" do
|
|
45
|
+
klass = @m::HashRow.subclass(:foo, [:a, :b, :c])
|
|
46
|
+
parser = @m::Parser.new(:converter=>klass, :columns=>[:a, :b, :c])
|
|
47
|
+
a = parser.call("(a,b,c)")
|
|
48
|
+
a.should be_a_kind_of(klass)
|
|
49
|
+
a.to_hash.should be_a_kind_of(Hash)
|
|
50
|
+
a[:a].should == 'a'
|
|
51
|
+
a.should == {:a=>'a', :b=>'b', :c=>'c'}
|
|
52
|
+
a.db_type.should == :foo
|
|
53
|
+
a.columns.should == [:a, :b, :c]
|
|
54
|
+
@db.literal(a).should == "ROW('a', 'b', 'c')::foo"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should raise an error if attempting to literalize a HashRow without column information" do
|
|
58
|
+
h = @m::HashRow.call(:a=>'a', :b=>'b', :c=>'c')
|
|
59
|
+
proc{@db.literal(h)}.should raise_error(Sequel::Error)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should be able to manually override db_type per ArrayRow instance" do
|
|
63
|
+
a = @m::ArrayRow.call(%w'a b c')
|
|
64
|
+
a.db_type = :foo
|
|
65
|
+
@db.literal(a).should == "ROW('a', 'b', 'c')::foo"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "should be able to manually override db_type and columns per HashRow instance" do
|
|
69
|
+
h = @m::HashRow.call(:a=>'a', :c=>'c', :b=>'b')
|
|
70
|
+
h.db_type = :foo
|
|
71
|
+
h.columns = [:a, :b, :c]
|
|
72
|
+
@db.literal(h).should == "ROW('a', 'b', 'c')::foo"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "should correctly split an empty row" do
|
|
76
|
+
@m::Splitter.new("()").parse.should == [nil]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "should correctly split a row with a single value" do
|
|
80
|
+
@m::Splitter.new("(1)").parse.should == %w'1'
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "should correctly split a row with multiple values" do
|
|
84
|
+
@m::Splitter.new("(1,2)").parse.should == %w'1 2'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should correctly NULL values when splitting" do
|
|
88
|
+
@m::Splitter.new("(1,)").parse.should == ['1', nil]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should correctly empty string values when splitting" do
|
|
92
|
+
@m::Splitter.new('(1,"")').parse.should == ['1', '']
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should handle quoted values when splitting" do
|
|
96
|
+
@m::Splitter.new('("1","2")').parse.should == %w'1 2'
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "should handle escaped backslashes in quoted values when splitting" do
|
|
100
|
+
@m::Splitter.new('("\\\\1","2\\\\")').parse.should == ['\\1', '2\\']
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should handle doubled quotes in quoted values when splitting" do
|
|
104
|
+
@m::Splitter.new('("""1","2""")').parse.should == ['"1', '2"']
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should correctly convert types when parsing into an array" do
|
|
108
|
+
@m::Parser.new(:column_converters=>[proc{|s| s*2}, proc{|s| s*3}, proc{|s| s*4}]).call("(a,b,c)").should == %w'aa bbb cccc'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "should correctly convert types into hashes if columns are known" do
|
|
112
|
+
@m::Parser.new(:columns=>[:a, :b, :c]).call("(a,b,c)").should == {:a=>'a', :b=>'b', :c=>'c'}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "should correctly handle type conversion when converting into hashes" do
|
|
116
|
+
@m::Parser.new(:column_converters=>[proc{|s| s*2}, proc{|s| s*3}, proc{|s| s*4}], :columns=>[:a, :b, :c]).call("(a,b,c)").should == {:a=>'aa', :b=>'bbb', :c=>'cccc'}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "should correctly wrap arrays when converting" do
|
|
120
|
+
@m::Parser.new(:converter=>proc{|s| [:foo, s]}).call("(a,b,c)").should == [:foo, %w'a b c']
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "should correctly wrap hashes when converting" do
|
|
124
|
+
@m::Parser.new(:converter=>proc{|s| [:foo, s]}, :columns=>[:a, :b, :c]).call("(a,b,c)").should == [:foo, {:a=>'a', :b=>'b', :c=>'c'}]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "should have parser store reflection information" do
|
|
128
|
+
p = @m::Parser.new(:oid=>1, :column_oids=>[2], :columns=>[:a], :converter=>Array, :typecaster=>Hash, :column_converters=>[Array])
|
|
129
|
+
p.oid.should == 1
|
|
130
|
+
p.column_oids.should == [2]
|
|
131
|
+
p.columns.should == [:a]
|
|
132
|
+
p.converter.should == Array
|
|
133
|
+
p.typecaster.should == Hash
|
|
134
|
+
p.column_converters.should == [Array]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "should reload registered row types when reseting conversion procs" do
|
|
138
|
+
db = Sequel.mock(:host=>'postgres')
|
|
139
|
+
db.extension(:pg_row)
|
|
140
|
+
db.conversion_procs[4] = p4 = proc{|s| s.to_i}
|
|
141
|
+
db.conversion_procs[5] = p5 = proc{|s| s * 2}
|
|
142
|
+
db.sqls
|
|
143
|
+
db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
|
|
144
|
+
db.register_row_type(:foo)
|
|
145
|
+
db.sqls.should == ["SELECT pg_type.oid, typrelid, typarray FROM pg_type WHERE ((typtype = 'c') AND (typname = 'foo')) LIMIT 1",
|
|
146
|
+
"SELECT attname, atttypid FROM pg_attribute WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "should handle ArrayRows and HashRows in bound variables" do
|
|
150
|
+
@db.bound_variable_arg(1, nil).should == 1
|
|
151
|
+
@db.bound_variable_arg(@m::ArrayRow.call(["1", "abc\\'\","]), nil).should == '("1","abc\\\\\'\\",")'
|
|
152
|
+
@db.bound_variable_arg(@m::HashRow.subclass(nil, [:a, :b]).call(:a=>"1", :b=>"abc\\'\","), nil).should == '("1","abc\\\\\'\\",")'
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "should handle ArrayRows and HashRows in arrays in bound variables" do
|
|
156
|
+
@db.bound_variable_arg(1, nil).should == 1
|
|
157
|
+
@db.bound_variable_arg([@m::ArrayRow.call(["1", "abc\\'\","])], nil).should == '{"(\\"1\\",\\"abc\\\\\\\\\'\\\\\\",\\")"}'
|
|
158
|
+
@db.bound_variable_arg([@m::HashRow.subclass(nil, [:a, :b]).call(:a=>"1", :b=>"abc\\'\",")], nil).should == '{"(\\"1\\",\\"abc\\\\\\\\\'\\\\\\",\\")"}'
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should allow registering row type parsers by introspecting system tables" do
|
|
162
|
+
@db.conversion_procs[4] = p4 = proc{|s| s.to_i}
|
|
163
|
+
@db.conversion_procs[5] = p5 = proc{|s| s * 2}
|
|
164
|
+
@db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
|
|
165
|
+
@db.register_row_type(:foo)
|
|
166
|
+
@db.sqls.should == ["SELECT pg_type.oid, typrelid, typarray FROM pg_type WHERE ((typtype = 'c') AND (typname = 'foo')) LIMIT 1",
|
|
167
|
+
"SELECT attname, atttypid FROM pg_attribute WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
|
|
168
|
+
p1 = @db.conversion_procs[1]
|
|
169
|
+
p1.columns.should == [:bar, :baz]
|
|
170
|
+
p1.column_oids.should == [4, 5]
|
|
171
|
+
p1.column_converters.should == [p4, p5]
|
|
172
|
+
p1.oid.should == 1
|
|
173
|
+
@db.send(:schema_column_type, 'foo').should == :pg_row_foo
|
|
174
|
+
@db.send(:schema_column_type, 'integer').should == :integer
|
|
175
|
+
|
|
176
|
+
c = p1.converter
|
|
177
|
+
c.superclass.should == @m::HashRow
|
|
178
|
+
c.columns.should == [:bar, :baz]
|
|
179
|
+
c.db_type.should == :foo
|
|
180
|
+
p1.typecaster.should == c
|
|
181
|
+
|
|
182
|
+
p1.call('(1,b)').should == {:bar=>1, :baz=>'bb'}
|
|
183
|
+
@db.typecast_value(:pg_row_foo, %w'1 b').should be_a_kind_of(@m::HashRow)
|
|
184
|
+
@db.typecast_value(:pg_row_foo, %w'1 b').should == {:bar=>'1', :baz=>'b'}
|
|
185
|
+
@db.typecast_value(:pg_row_foo, :bar=>'1', :baz=>'b').should == {:bar=>'1', :baz=>'b'}
|
|
186
|
+
@db.literal(p1.call('(1,b)')).should == "ROW(1, 'bb')::foo"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "should allow registering row type parsers for schema qualify types" do
|
|
190
|
+
@db.conversion_procs[4] = p4 = proc{|s| s.to_i}
|
|
191
|
+
@db.conversion_procs[5] = p5 = proc{|s| s * 2}
|
|
192
|
+
@db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
|
|
193
|
+
@db.register_row_type(:foo__bar)
|
|
194
|
+
@db.sqls.should == ["SELECT pg_type.oid, typrelid, typarray FROM pg_type INNER JOIN pg_namespace ON ((pg_namespace.oid = pg_type.typnamespace) AND (pg_namespace.nspname = 'foo')) WHERE ((typtype = 'c') AND (typname = 'bar')) LIMIT 1",
|
|
195
|
+
"SELECT attname, atttypid FROM pg_attribute WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
|
|
196
|
+
p1 = @db.conversion_procs[1]
|
|
197
|
+
p1.columns.should == [:bar, :baz]
|
|
198
|
+
p1.column_oids.should == [4, 5]
|
|
199
|
+
p1.column_converters.should == [p4, p5]
|
|
200
|
+
p1.oid.should == 1
|
|
201
|
+
|
|
202
|
+
c = p1.converter
|
|
203
|
+
c.superclass.should == @m::HashRow
|
|
204
|
+
c.columns.should == [:bar, :baz]
|
|
205
|
+
c.db_type.should == :foo__bar
|
|
206
|
+
p1.typecaster.should == c
|
|
207
|
+
|
|
208
|
+
p1.call('(1,b)').should == {:bar=>1, :baz=>'bb'}
|
|
209
|
+
@db.typecast_value(:pg_row_foo__bar, %w'1 b').should == {:bar=>'1', :baz=>'b'}
|
|
210
|
+
@db.typecast_value(:pg_row_foo__bar, :bar=>'1', :baz=>'b').should == {:bar=>'1', :baz=>'b'}
|
|
211
|
+
@db.literal(p1.call('(1,b)')).should == "ROW(1, 'bb')::foo.bar"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it "should allow registering with a custom converter" do
|
|
215
|
+
@db.conversion_procs[4] = p4 = proc{|s| s.to_i}
|
|
216
|
+
@db.conversion_procs[5] = p5 = proc{|s| s * 2}
|
|
217
|
+
@db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
|
|
218
|
+
c = proc{|h| [h]}
|
|
219
|
+
@db.register_row_type(:foo, :converter=>c)
|
|
220
|
+
o = @db.conversion_procs[1].call('(1,b)')
|
|
221
|
+
o.should == [{:bar=>1, :baz=>'bb'}]
|
|
222
|
+
o.first.should be_a_kind_of(Hash)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it "should allow registering with a custom typecaster" do
|
|
226
|
+
@db.conversion_procs[4] = p4 = proc{|s| s.to_i}
|
|
227
|
+
@db.conversion_procs[5] = p5 = proc{|s| s * 2}
|
|
228
|
+
@db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
|
|
229
|
+
@db.register_row_type(:foo, :typecaster=>proc{|h| {:bar=>(h[:bar]||0).to_i, :baz=>(h[:baz] || 'a')*2}})
|
|
230
|
+
@db.typecast_value(:pg_row_foo, %w'1 b').should be_a_kind_of(Hash)
|
|
231
|
+
@db.typecast_value(:pg_row_foo, %w'1 b').should == {:bar=>1, :baz=>'bb'}
|
|
232
|
+
@db.typecast_value(:pg_row_foo, :bar=>'1', :baz=>'b').should == {:bar=>1, :baz=>'bb'}
|
|
233
|
+
@db.typecast_value(:pg_row_foo, 'bar'=>'1', 'baz'=>'b').should == {:bar=>0, :baz=>'aa'}
|
|
234
|
+
@db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
|
|
235
|
+
@db.register_row_type(:foo, :typecaster=>proc{|h| {:bar=>(h[:bar] || h['bar'] || 0).to_i, :baz=>(h[:baz] || h['baz'] || 'a')*2}})
|
|
236
|
+
@db.typecast_value(:pg_row_foo, %w'1 b').should == {:bar=>1, :baz=>'bb'}
|
|
237
|
+
@db.typecast_value(:pg_row_foo, :bar=>'1', :baz=>'b').should == {:bar=>1, :baz=>'bb'}
|
|
238
|
+
@db.typecast_value(:pg_row_foo, 'bar'=>'1', 'baz'=>'b').should == {:bar=>1, :baz=>'bb'}
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
it "should handle conversion procs that aren't added until later" do
|
|
242
|
+
@db.conversion_procs[5] = p5 = proc{|s| s * 2}
|
|
243
|
+
@db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
|
|
244
|
+
c = proc{|h| [h]}
|
|
245
|
+
@db.register_row_type(:foo, :converter=>c)
|
|
246
|
+
@db.conversion_procs[4] = p4 = proc{|s| s.to_i}
|
|
247
|
+
@db.conversion_procs[1].call('(1,b)').should == [{:bar=>1, :baz=>'bb'}]
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
it "should registering array type for row type if type has an array oid" do
|
|
251
|
+
@db.conversion_procs[4] = p4 = proc{|s| s.to_i}
|
|
252
|
+
@db.conversion_procs[5] = p5 = proc{|s| s * 2}
|
|
253
|
+
@db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
|
|
254
|
+
@db.register_row_type(:foo, :typecaster=>proc{|h| {:bar=>(h[:bar]||0).to_i, :baz=>(h[:baz] || 'a')*2}})
|
|
255
|
+
p3 = @db.conversion_procs[3]
|
|
256
|
+
|
|
257
|
+
p3.call('{"(1,b)"}').should == [{:bar=>1, :baz=>'bb'}]
|
|
258
|
+
@db.literal(p3.call('{"(1,b)"}')).should == "ARRAY[ROW(1, 'bb')::foo]::foo[]"
|
|
259
|
+
@db.typecast_value(:foo_array, [{:bar=>'1', :baz=>'b'}]).should == [{:bar=>1, :baz=>'bb'}]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
it "should allow creating unregisted row types via Database#row_type" do
|
|
263
|
+
@db.literal(@db.row_type(:foo, [1, 2])).should == 'ROW(1, 2)::foo'
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it "should allow typecasting of registered row types via Database#row_type" do
|
|
267
|
+
@db.conversion_procs[4] = p4 = proc{|s| s.to_i}
|
|
268
|
+
@db.conversion_procs[5] = p5 = proc{|s| s * 2}
|
|
269
|
+
@db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
|
|
270
|
+
@db.register_row_type(:foo, :typecaster=>proc{|h| @m::HashRow.subclass(:foo, [:bar, :baz]).new({:bar=>(h[:bar]||0).to_i, :baz=>(h[:baz] || 'a')*2})})
|
|
271
|
+
@db.literal(@db.row_type(:foo, ['1', 'b'])).should == "ROW(1, 'bb')::foo"
|
|
272
|
+
@db.literal(@db.row_type(:foo, {:bar=>'1', :baz=>'b'})).should == "ROW(1, 'bb')::foo"
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
it "should allow parsing when typecasting registered row types via Database#row_type" do
|
|
276
|
+
@db.conversion_procs[4] = p4 = proc{|s| s.to_i}
|
|
277
|
+
@db.conversion_procs[5] = p5 = proc{|s| s * 2}
|
|
278
|
+
@db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
|
|
279
|
+
@db.register_row_type(:foo, :typecaster=>proc{|h| @m::HashRow.subclass(:foo, [:bar, :baz]).new(:bar=>(h[:bar]||0).to_i, :baz=>(h[:baz] || 'a')*2)})
|
|
280
|
+
@db.literal(@db.row_type(:foo, ['1', 'b'])).should == "ROW(1, 'bb')::foo"
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
it "should raise an error if attempt to use Database#row_type with an unregistered type and hash" do
|
|
284
|
+
proc{@db.literal(@db.row_type(:foo, {:bar=>'1', :baz=>'b'}))}.should raise_error(Sequel::Error)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
it "should raise an error if attempt to use Database#row_type with an unhandled type" do
|
|
288
|
+
proc{@db.literal(@db.row_type(:foo, 1))}.should raise_error(Sequel::Error)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
it "should return ArrayRow and HashRow values as-is" do
|
|
292
|
+
h = @m::HashRow.call(:a=>1)
|
|
293
|
+
a = @m::ArrayRow.call([1])
|
|
294
|
+
@db.row_type(:foo, h).should equal(h)
|
|
295
|
+
@db.row_type(:foo, a).should equal(a)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
it "should have Sequel.pg_row return a plain ArrayRow" do
|
|
299
|
+
@db.literal(Sequel.pg_row([1, 2, 3])).should == 'ROW(1, 2, 3)'
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it "should raise an error if attempting to typecast a hash for a parser without columns" do
|
|
303
|
+
proc{@m::Parser.new.typecast(:a=>1)}.should raise_error(Sequel::Error)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it "should raise an error if attempting to typecast a unhandled value for a parser" do
|
|
307
|
+
proc{@m::Parser.new.typecast(1)}.should raise_error(Sequel::Error)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
it "should handle typecasting for a parser without a typecaster" do
|
|
311
|
+
@m::Parser.new.typecast([1]).should == [1]
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
it "should raise an error if no columns are returned when registering a custom row type" do
|
|
315
|
+
@db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}]]
|
|
316
|
+
proc{@db.register_row_type(:foo)}.should raise_error(Sequel::Error)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
it "should raise an error when registering a custom row type if the type is found found" do
|
|
320
|
+
@db.fetch = []
|
|
321
|
+
proc{@db.register_row_type(:foo)}.should raise_error(Sequel::Error)
|
|
322
|
+
end
|
|
323
|
+
end
|