sequel 3.33.0 → 3.34.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +140 -0
- data/Rakefile +7 -0
- data/bin/sequel +22 -2
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/mass_assignment.rdoc +3 -1
- data/doc/querying.rdoc +28 -4
- data/doc/reflection.rdoc +23 -3
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/schema_modification.rdoc +18 -2
- data/doc/virtual_rows.rdoc +49 -0
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/ibmdb.rb +9 -4
- data/lib/sequel/adapters/jdbc.rb +9 -4
- data/lib/sequel/adapters/jdbc/h2.rb +8 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +43 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +19 -0
- data/lib/sequel/adapters/mock.rb +24 -3
- data/lib/sequel/adapters/mysql.rb +29 -50
- data/lib/sequel/adapters/mysql2.rb +13 -28
- data/lib/sequel/adapters/oracle.rb +8 -2
- data/lib/sequel/adapters/postgres.rb +115 -20
- data/lib/sequel/adapters/shared/db2.rb +1 -1
- data/lib/sequel/adapters/shared/mssql.rb +14 -3
- data/lib/sequel/adapters/shared/mysql.rb +59 -11
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +1 -1
- data/lib/sequel/adapters/shared/postgres.rb +127 -30
- data/lib/sequel/adapters/shared/sqlite.rb +55 -38
- data/lib/sequel/adapters/sqlite.rb +9 -3
- data/lib/sequel/adapters/swift.rb +2 -2
- data/lib/sequel/adapters/swift/mysql.rb +0 -5
- data/lib/sequel/adapters/swift/postgres.rb +10 -0
- data/lib/sequel/ast_transformer.rb +4 -0
- data/lib/sequel/connection_pool.rb +8 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +17 -0
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +14 -0
- data/lib/sequel/core.rb +24 -3
- data/lib/sequel/database/connecting.rb +24 -14
- data/lib/sequel/database/dataset_defaults.rb +1 -0
- data/lib/sequel/database/misc.rb +16 -25
- data/lib/sequel/database/query.rb +20 -2
- data/lib/sequel/database/schema_generator.rb +2 -2
- data/lib/sequel/database/schema_methods.rb +120 -23
- data/lib/sequel/dataset/actions.rb +91 -18
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/prepared_statements.rb +6 -2
- data/lib/sequel/dataset/sql.rb +68 -51
- data/lib/sequel/extensions/_pretty_table.rb +79 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +18 -13
- data/lib/sequel/extensions/migration.rb +4 -0
- data/lib/sequel/extensions/null_dataset.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +460 -0
- data/lib/sequel/extensions/pg_array_ops.rb +220 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +174 -0
- data/lib/sequel/extensions/pg_hstore.rb +296 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
- data/lib/sequel/extensions/pretty_table.rb +5 -71
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +227 -31
- data/lib/sequel/extensions/select_remove.rb +35 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -110
- data/lib/sequel/extensions/to_dot.rb +1 -1
- data/lib/sequel/model.rb +11 -2
- data/lib/sequel/model/associations.rb +35 -7
- data/lib/sequel/model/base.rb +159 -36
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/composition.rb +1 -1
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +11 -3
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +4 -3
- data/lib/sequel/plugins/prepared_statements.rb +3 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +5 -1
- data/lib/sequel/plugins/schema.rb +7 -2
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/sql.rb +417 -7
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +12 -15
- data/spec/adapters/mysql_spec.rb +81 -23
- data/spec/adapters/postgres_spec.rb +444 -77
- data/spec/adapters/spec_helper.rb +2 -0
- data/spec/adapters/sqlite_spec.rb +8 -8
- data/spec/core/connection_pool_spec.rb +85 -0
- data/spec/core/database_spec.rb +29 -5
- data/spec/core/dataset_spec.rb +171 -3
- data/spec/core/expression_filters_spec.rb +364 -0
- data/spec/core/mock_adapter_spec.rb +17 -3
- data/spec/core/schema_spec.rb +133 -0
- data/spec/extensions/association_dependencies_spec.rb +13 -13
- data/spec/extensions/caching_spec.rb +26 -3
- data/spec/extensions/class_table_inheritance_spec.rb +2 -2
- data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +23 -94
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +5 -2
- data/spec/extensions/identity_map_spec.rb +17 -0
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/lazy_attributes_spec.rb +2 -2
- data/spec/extensions/list_spec.rb +4 -4
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +6 -2
- data/spec/extensions/nested_attributes_spec.rb +20 -0
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +2 -2
- data/spec/extensions/pg_array_ops_spec.rb +105 -0
- data/spec/extensions/pg_array_spec.rb +196 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +64 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
- data/spec/extensions/pg_hstore_spec.rb +195 -0
- data/spec/extensions/pg_statement_cache_spec.rb +209 -0
- data/spec/extensions/prepared_statements_spec.rb +4 -0
- data/spec/extensions/pretty_table_spec.rb +6 -0
- data/spec/extensions/query_literals_spec.rb +168 -0
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +231 -11
- data/spec/extensions/schema_spec.rb +14 -2
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/sharding_spec.rb +6 -6
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +2 -1
- data/spec/extensions/sql_expr_spec.rb +28 -19
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/typecast_on_load_spec.rb +9 -1
- data/spec/integration/associations_test.rb +6 -6
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +89 -26
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +3 -3
- data/spec/integration/plugin_test.rb +85 -22
- data/spec/integration/prepared_statement_test.rb +28 -8
- data/spec/integration/schema_test.rb +78 -7
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +4 -6
- data/spec/integration/type_test.rb +2 -2
- data/spec/model/associations_spec.rb +94 -8
- data/spec/model/base_spec.rb +4 -4
- data/spec/model/hooks_spec.rb +2 -2
- data/spec/model/model_spec.rb +19 -7
- data/spec/model/record_spec.rb +135 -58
- data/spec/model/spec_helper.rb +1 -0
- metadata +35 -7
@@ -129,6 +129,7 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
|
|
129
129
|
rename_column :e, :g
|
130
130
|
end
|
131
131
|
create_view(:c, 'SELECT * FROM b')
|
132
|
+
create_join_table(:cat_id=>:cats, :dog_id=>:dogs)
|
132
133
|
end
|
133
134
|
end
|
134
135
|
|
@@ -150,13 +151,16 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
|
|
150
151
|
[:add_spatial_index, :e, {:name=>"e_s"}],
|
151
152
|
[:rename_column, :e, :g]]
|
152
153
|
],
|
153
|
-
[:create_view, :c, "SELECT * FROM b"]
|
154
|
+
[:create_view, :c, "SELECT * FROM b"],
|
155
|
+
[:create_join_table, {:cat_id=>:cats, :dog_id=>:dogs}]]
|
154
156
|
end
|
155
157
|
|
156
158
|
specify "should execute down with reversing actions in reverse order" do
|
157
159
|
p = @p
|
158
160
|
Sequel.migration{change(&p)}.apply(@db, :down)
|
159
|
-
@db.actions.should == [
|
161
|
+
@db.actions.should == [
|
162
|
+
[:drop_join_table, {:cat_id=>:cats, :dog_id=>:dogs}],
|
163
|
+
[:drop_view, :c],
|
160
164
|
[:alter_table, [
|
161
165
|
[:rename_column, :g, :e],
|
162
166
|
[:drop_index, :e, {:name=>"e_s"}],
|
@@ -110,6 +110,16 @@ describe "NestedAttributes plugin" do
|
|
110
110
|
@db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
|
111
111
|
end
|
112
112
|
|
113
|
+
it "should support updating one_to_many objects with _delete/_remove flags set to false" do
|
114
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
115
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
116
|
+
ar.associations[:albums] = [al]
|
117
|
+
ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2', :_delete => 'f', :_remove => '0'}])
|
118
|
+
@db.sqls.should == []
|
119
|
+
ar.save
|
120
|
+
@db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
|
121
|
+
end
|
122
|
+
|
113
123
|
it "should support updating many_to_many objects" do
|
114
124
|
a = @Album.load(:id=>10, :name=>'Al')
|
115
125
|
t = @Tag.load(:id=>20, :name=>'T')
|
@@ -120,6 +130,16 @@ describe "NestedAttributes plugin" do
|
|
120
130
|
@db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
|
121
131
|
end
|
122
132
|
|
133
|
+
it "should support updating many_to_many objects with _delete/_remove flags set to false" do
|
134
|
+
a = @Album.load(:id=>10, :name=>'Al')
|
135
|
+
t = @Tag.load(:id=>20, :name=>'T')
|
136
|
+
a.associations[:tags] = [t]
|
137
|
+
a.set(:tags_attributes=>[{:id=>20, :name=>'T2', '_delete' => false, '_remove' => 'F'}])
|
138
|
+
@db.sqls.should == []
|
139
|
+
a.save
|
140
|
+
@db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
|
141
|
+
end
|
142
|
+
|
123
143
|
it "should support removing many_to_one objects" do
|
124
144
|
al = @Album.load(:id=>10, :name=>'Al')
|
125
145
|
ar = @Artist.load(:id=>20, :name=>'Ar')
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
|
+
|
3
|
+
describe "null_dataset extension" do
|
4
|
+
before do
|
5
|
+
@db = Sequel::mock(:fetch=>{:id=>1}, :autoid=>1, :numrows=>1, :columns=>[:id])
|
6
|
+
@ds = @db[:table].nullify
|
7
|
+
@i = 0
|
8
|
+
@pr = proc{|*a| @i += 1}
|
9
|
+
end
|
10
|
+
after do
|
11
|
+
@db.sqls.should == [] unless @skip_check
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should make each be a noop" do
|
15
|
+
@ds.each(&@pr).should equal(@ds)
|
16
|
+
@i.should == 0
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should make fetch_rows be a noop" do
|
20
|
+
@ds.fetch_rows("SELECT 1", &@pr).should == nil
|
21
|
+
@i.should == 0
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should make insert be a noop" do
|
25
|
+
@ds.insert(1).should == nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should make update be a noop" do
|
29
|
+
@ds.update(:a=>1).should == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should make delete be a noop" do
|
33
|
+
@ds.delete.should == 0
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should make truncate be a noop" do
|
37
|
+
@ds.truncate.should == nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should make execute_* be a noop" do
|
41
|
+
@ds.send(:execute_ddl,'FOO').should == nil
|
42
|
+
@ds.send(:execute_insert,'FOO').should == nil
|
43
|
+
@ds.send(:execute_dui,'FOO').should == nil
|
44
|
+
@ds.send(:execute,'FOO').should == nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should have working columns" do
|
48
|
+
@skip_check = true
|
49
|
+
@ds.columns.should == [:id]
|
50
|
+
@db.sqls.should == ['SELECT * FROM table LIMIT 1']
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should have count return 0" do
|
54
|
+
@ds.count.should == 0
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should have empty return true" do
|
58
|
+
@ds.empty?.should == true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should make import a noop" do
|
62
|
+
@ds.import([:id], [[1], [2], [3]]).should == nil
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should have nullify method returned modified receiver" do
|
66
|
+
@skip_check = true
|
67
|
+
ds = @db[:table]
|
68
|
+
ds.nullify.should_not equal(ds)
|
69
|
+
ds.each(&@pr)
|
70
|
+
@db.sqls.should == ['SELECT * FROM table']
|
71
|
+
@i.should == 1
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should have null! method modify receiver" do
|
75
|
+
ds = @db[:table]
|
76
|
+
ds.nullify!.should equal(ds)
|
77
|
+
ds.each(&@pr)
|
78
|
+
@i.should == 0
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should work with method chaining" do
|
82
|
+
@ds.where(:a=>1).select(:b).each(&@pr)
|
83
|
+
@i.should == 0
|
84
|
+
end
|
85
|
+
end
|
@@ -6,7 +6,7 @@ describe "optimistic_locking plugin" do
|
|
6
6
|
end
|
7
7
|
h = {1=>{:id=>1, :name=>'John', :lock_version=>2}}
|
8
8
|
lv = @lv = "lock_version"
|
9
|
-
@c.dataset.numrows = proc do |sql|
|
9
|
+
@c.instance_dataset.numrows = @c.dataset.numrows = proc do |sql|
|
10
10
|
case sql
|
11
11
|
when /UPDATE people SET (name|#{lv}) = ('Jim'|'Bob'|\d+), (?:name|#{lv}) = ('Jim'|'Bob'|\d+) WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
|
12
12
|
name, nlv = $1 == 'name' ? [$2, $3] : [$3, $2]
|
@@ -104,7 +104,7 @@ describe "optimistic_locking plugin" do
|
|
104
104
|
end
|
105
105
|
|
106
106
|
specify "should not increment the lock column when the update fails" do
|
107
|
-
@c.
|
107
|
+
@c.instance_dataset.meta_def(:update) { raise Exception }
|
108
108
|
p1 = @c[1]
|
109
109
|
p1.modified!
|
110
110
|
lv = p1.lock_version
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
|
+
|
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
|
+
before do
|
15
|
+
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
16
|
+
@a = :a.pg_array
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should support the standard mathematical operators" do
|
20
|
+
@db.literal(@a < @a).should == "(a < a)"
|
21
|
+
@db.literal(@a <= @a).should == "(a <= a)"
|
22
|
+
@db.literal(@a > @a).should == "(a > a)"
|
23
|
+
@db.literal(@a >= @a).should == "(a >= a)"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "#[] should support subscript access" do
|
27
|
+
@db.literal(@a[1]).should == "a[1]"
|
28
|
+
@db.literal(@a[1][2]).should == "a[1][2]"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "#any should use the ANY method" do
|
32
|
+
@db.literal(1=>@a.any).should == "(1 = ANY(a))"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "#all should use the ALL method" do
|
36
|
+
@db.literal(1=>@a.all).should == "(1 = ALL(a))"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "#contains should use the @> operator" do
|
40
|
+
@db.literal(@a.contains(:b)).should == "(a @> b)"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "#contained_by should use the <@ operator" do
|
44
|
+
@db.literal(@a.contained_by(:b)).should == "(a <@ b)"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "#overlaps should use the && operator" do
|
48
|
+
@db.literal(@a.overlaps(:b)).should == "(a && b)"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "#push/concat should use the || operator in append mode" do
|
52
|
+
@db.literal(@a.push(:b)).should == "(a || b)"
|
53
|
+
@db.literal(@a.concat(:b)).should == "(a || b)"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "#unshift should use the || operator in prepend mode" do
|
57
|
+
@db.literal(@a.unshift(:b)).should == "(b || a)"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "#dims should use the array_dims function" do
|
61
|
+
@db.literal(@a.dims).should == "array_dims(a)"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "#length should use the array_length function" do
|
65
|
+
@db.literal(@a.length).should == "array_length(a, 1)"
|
66
|
+
@db.literal(@a.length(2)).should == "array_length(a, 2)"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "#length should use the array_lower function" do
|
70
|
+
@db.literal(@a.lower).should == "array_lower(a, 1)"
|
71
|
+
@db.literal(@a.lower(2)).should == "array_lower(a, 2)"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "#to_string/join should use the array_to_string function" do
|
75
|
+
@db.literal(@a.to_string).should == "array_to_string(a, '', NULL)"
|
76
|
+
@db.literal(@a.join).should == "array_to_string(a, '', NULL)"
|
77
|
+
@db.literal(@a.join(':')).should == "array_to_string(a, ':', NULL)"
|
78
|
+
@db.literal(@a.join(':', '*')).should == "array_to_string(a, ':', '*')"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "#unnest should use the unnest function" do
|
82
|
+
@db.literal(@a.unnest).should == "unnest(a)"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "#pg_array should return self" do
|
86
|
+
@a.pg_array.should equal(@a)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should be able to turn expressions into array ops using pg_array" do
|
90
|
+
@db.literal(:a.qualify(:b).pg_array.push(3)).should == "(b.a || 3)"
|
91
|
+
@db.literal(:a.sql_function(:b).pg_array.push(3)).should == "(a(b) || 3)"
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should be able to turn literal strings into array ops using pg_array" do
|
95
|
+
@db.literal('a'.lit.pg_array.unnest).should == "unnest(a)"
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should be able to turn symbols into array ops using pg_array" do
|
99
|
+
@db.literal(:a.pg_array.unnest).should == "unnest(a)"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should allow transforming PGArray instances into ArrayOp instances" do
|
103
|
+
@db.literal([1,2].pg_array.op.push(3)).should == "(ARRAY[1,2] || 3)"
|
104
|
+
end if pg_array_loaded
|
105
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
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
|
+
describe "pg_array extension" do
|
9
|
+
before do
|
10
|
+
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
11
|
+
@db.extend(Module.new{def bound_variable_arg(arg, conn) arg end})
|
12
|
+
@m = Sequel::Postgres
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should parse single dimensional text arrays" do
|
16
|
+
@m::PGStringArray.parse("{a}").to_a.first.should be_a_kind_of(String)
|
17
|
+
@m::PGStringArray.parse("{}").to_a.should == []
|
18
|
+
@m::PGStringArray.parse("{a}").to_a.should == ['a']
|
19
|
+
@m::PGStringArray.parse('{"a b"}').to_a.should == ['a b']
|
20
|
+
@m::PGStringArray.parse('{a,b}').to_a.should == ['a', 'b']
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should parse multi-dimensional text arrays" do
|
24
|
+
@m::PGStringArray.parse("{{}}").to_a.should == [[]]
|
25
|
+
@m::PGStringArray.parse("{{a},{b}}").to_a.should == [['a'], ['b']]
|
26
|
+
@m::PGStringArray.parse('{{"a b"},{c}}').to_a.should == [['a b'], ['c']]
|
27
|
+
@m::PGStringArray.parse('{{{a},{b}},{{c},{d}}}').to_a.should == [[['a'], ['b']], [['c'], ['d']]]
|
28
|
+
@m::PGStringArray.parse('{{{a,e},{b,f}},{{c,g},{d,h}}}').to_a.should == [[['a', 'e'], ['b', 'f']], [['c', 'g'], ['d', 'h']]]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should parse text arrays with embedded deliminaters" do
|
32
|
+
@m::PGStringArray.parse('{{"{},","\\",\\,\\\\\\"\\""}}').to_a.should == [['{},', '",,\\""']]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should parse single dimensional integer arrays" do
|
36
|
+
@m::PGIntegerArray.parse("{1}").to_a.first.should be_a_kind_of(Integer)
|
37
|
+
@m::PGIntegerArray.parse("{}").to_a.should == []
|
38
|
+
@m::PGIntegerArray.parse("{1}").to_a.should == [1]
|
39
|
+
@m::PGIntegerArray.parse('{2,3}').to_a.should == [2, 3]
|
40
|
+
@m::PGIntegerArray.parse('{3,4,5}').to_a.should == [3, 4, 5]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should parse multiple dimensional integer arrays" do
|
44
|
+
@m::PGIntegerArray.parse("{{}}").to_a.should == [[]]
|
45
|
+
@m::PGIntegerArray.parse("{{1}}").to_a.should == [[1]]
|
46
|
+
@m::PGIntegerArray.parse('{{2},{3}}').to_a.should == [[2], [3]]
|
47
|
+
@m::PGIntegerArray.parse('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should parse single dimensional float arrays" do
|
51
|
+
@m::PGFloatArray.parse("{}").to_a.should == []
|
52
|
+
@m::PGFloatArray.parse("{1.5}").to_a.should == [1.5]
|
53
|
+
@m::PGFloatArray.parse('{2.5,3.5}').to_a.should == [2.5, 3.5]
|
54
|
+
@m::PGFloatArray.parse('{3.5,4.5,5.5}').to_a.should == [3.5, 4.5, 5.5]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should parse multiple dimensional float arrays" do
|
58
|
+
@m::PGFloatArray.parse("{{}}").to_a.should == [[]]
|
59
|
+
@m::PGFloatArray.parse("{{1.5}}").to_a.should == [[1.5]]
|
60
|
+
@m::PGFloatArray.parse('{{2.5},{3.5}}').to_a.should == [[2.5], [3.5]]
|
61
|
+
@m::PGFloatArray.parse('{{{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
|
+
end
|
63
|
+
|
64
|
+
it "should parse integers in float arrays as floats" do
|
65
|
+
@m::PGFloatArray.parse("{1}").to_a.first.should be_a_kind_of(Float)
|
66
|
+
@m::PGFloatArray.parse("{1}").to_a.should == [1.0]
|
67
|
+
@m::PGFloatArray.parse('{{{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
|
+
end
|
69
|
+
|
70
|
+
it "should parse single dimensional decimal arrays" do
|
71
|
+
@m::PGDecimalArray.parse("{}").to_a.should == []
|
72
|
+
@m::PGDecimalArray.parse("{1.5}").to_a.should == [BigDecimal.new('1.5')]
|
73
|
+
@m::PGDecimalArray.parse('{2.5,3.5}').to_a.should == [BigDecimal.new('2.5'), BigDecimal.new('3.5')]
|
74
|
+
@m::PGDecimalArray.parse('{3.5,4.5,5.5}').to_a.should == [BigDecimal.new('3.5'), BigDecimal.new('4.5'), BigDecimal.new('5.5')]
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should parse multiple dimensional decimal arrays" do
|
78
|
+
@m::PGDecimalArray.parse("{{}}").to_a.should == [[]]
|
79
|
+
@m::PGDecimalArray.parse("{{1.5}}").to_a.should == [[BigDecimal.new('1.5')]]
|
80
|
+
@m::PGDecimalArray.parse('{{2.5},{3.5}}').to_a.should == [[BigDecimal.new('2.5')], [BigDecimal.new('3.5')]]
|
81
|
+
@m::PGDecimalArray.parse('{{{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
|
+
end
|
83
|
+
|
84
|
+
it "should parse decimal values with arbitrary precision" do
|
85
|
+
@m::PGDecimalArray.parse("{1.000000000000000000005}").to_a.should == [BigDecimal.new('1.000000000000000000005')]
|
86
|
+
@m::PGDecimalArray.parse("{{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
|
+
end
|
88
|
+
|
89
|
+
it "should parse integers in decimal arrays as BigDecimals" do
|
90
|
+
@m::PGDecimalArray.parse("{1}").to_a.first.should be_a_kind_of(BigDecimal)
|
91
|
+
@m::PGDecimalArray.parse("{1}").to_a.should == [BigDecimal.new('1')]
|
92
|
+
@m::PGDecimalArray.parse('{{{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
|
+
end
|
94
|
+
|
95
|
+
it "should parse arrays with NULL values" do
|
96
|
+
[@m::PGStringArray, @m::PGIntegerArray, @m::PGFloatArray, @m::PGDecimalArray].each do |c|
|
97
|
+
c.parse("{NULL}").should == [nil]
|
98
|
+
c.parse("{NULL,NULL}").should == [nil,nil]
|
99
|
+
c.parse("{{NULL,NULL},{NULL,NULL}}").should == [[nil,nil],[nil,nil]]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should parse arrays with "NULL" values' do
|
104
|
+
@m::PGStringArray.parse('{NULL,"NULL",NULL}').to_a.should == [nil, "NULL", nil]
|
105
|
+
@m::PGStringArray.parse('{NULLA,"NULL",NULL}').to_a.should == ["NULLA", "NULL", nil]
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should literalize arrays without types correctly" do
|
109
|
+
@db.literal(@m::PGArray.new([])).should == 'ARRAY[]'
|
110
|
+
@db.literal(@m::PGArray.new([1])).should == 'ARRAY[1]'
|
111
|
+
@db.literal(@m::PGArray.new([nil])).should == 'ARRAY[NULL]'
|
112
|
+
@db.literal(@m::PGArray.new([nil, 1])).should == 'ARRAY[NULL,1]'
|
113
|
+
@db.literal(@m::PGArray.new([1.0, 2.5])).should == 'ARRAY[1.0,2.5]'
|
114
|
+
@db.literal(@m::PGArray.new([BigDecimal.new('1'), BigDecimal.new('2.000000000000000000005')])).should == 'ARRAY[1.0,2.000000000000000000005]'
|
115
|
+
@db.literal(@m::PGArray.new([nil, "NULL"])).should == "ARRAY[NULL,'NULL']"
|
116
|
+
@db.literal(@m::PGArray.new([nil, "{},[]'\""])).should == "ARRAY[NULL,'{},[]''\"']"
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should literalize multidimensional arrays correctly" do
|
120
|
+
@db.literal(@m::PGArray.new([[]])).should == 'ARRAY[[]]'
|
121
|
+
@db.literal(@m::PGArray.new([[1, 2]])).should == 'ARRAY[[1,2]]'
|
122
|
+
@db.literal(@m::PGArray.new([[3], [5]])).should == 'ARRAY[[3],[5]]'
|
123
|
+
@db.literal(@m::PGArray.new([[[1.0]], [[2.5]]])).should == 'ARRAY[[[1.0]],[[2.5]]]'
|
124
|
+
@db.literal(@m::PGArray.new([[[["NULL"]]]])).should == "ARRAY[[[['NULL']]]]"
|
125
|
+
@db.literal(@m::PGArray.new([["a", "b"], ["{},[]'\"", nil]])).should == "ARRAY[['a','b'],['{},[]''\"',NULL]]"
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should literalize with types correctly" do
|
129
|
+
@db.literal(@m::PGArray.new([1], :int4)).should == 'ARRAY[1]::int4[]'
|
130
|
+
@db.literal(@m::PGArray.new([nil], :text)).should == 'ARRAY[NULL]::text[]'
|
131
|
+
@db.literal(@m::PGArray.new([nil, 1], :int8)).should == 'ARRAY[NULL,1]::int8[]'
|
132
|
+
@db.literal(@m::PGArray.new([1.0, 2.5], :real)).should == 'ARRAY[1.0,2.5]::real[]'
|
133
|
+
@db.literal(@m::PGArray.new([BigDecimal.new('1'), BigDecimal.new('2.000000000000000000005')], :decimal)).should == 'ARRAY[1.0,2.000000000000000000005]::decimal[]'
|
134
|
+
@db.literal(@m::PGArray.new([nil, "NULL"], :varchar)).should == "ARRAY[NULL,'NULL']::varchar[]"
|
135
|
+
@db.literal(@m::PGArray.new([nil, "{},[]'\""], :"varchar(255)")).should == "ARRAY[NULL,'{},[]''\"']::varchar(255)[]"
|
136
|
+
end
|
137
|
+
|
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
|
+
it "should have Array#pg_array method for easy PGArray creation" do
|
157
|
+
@db.literal([1].pg_array).should == 'ARRAY[1]'
|
158
|
+
@db.literal([1, 2].pg_array(:int4)).should == 'ARRAY[1,2]::int4[]'
|
159
|
+
@db.literal([[[1], [2]], [[3], [4]]].pg_array(:real)).should == 'ARRAY[[[1],[2]],[[3],[4]]]::real[]'
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should support using arrays as bound variables" do
|
163
|
+
@db.extend Sequel::Postgres::PGArray::DatabaseMethods
|
164
|
+
@db.bound_variable_arg(1, nil).should == 1
|
165
|
+
@db.bound_variable_arg([1,2].pg_array, nil).should == '{1,2}'
|
166
|
+
@db.bound_variable_arg([1,2], nil).should == '{1,2}'
|
167
|
+
@db.bound_variable_arg([[1,2]], nil).should == '{{1,2}}'
|
168
|
+
@db.bound_variable_arg([1.0,2.0], nil).should == '{1.0,2.0}'
|
169
|
+
@db.bound_variable_arg(["\\ \"", 'NULL', nil], nil).should == '{"\\\\ \\"","NULL",NULL}'
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should parse array types from the schema correctly" do
|
173
|
+
@db.extend Sequel::Postgres::PGArray::DatabaseMethods
|
174
|
+
@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
|
+
@db.schema(:items).map{|e| e[1][:type]}.should == [:integer, :integer_array, :float_array, :decimal_array, :string_array]
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should support typecasting of the various array types" do
|
179
|
+
@db.extend Sequel::Postgres::PGArray::DatabaseMethods
|
180
|
+
a = [1, 2]
|
181
|
+
o = a.pg_array
|
182
|
+
%w[integer float decimal string].each do |x|
|
183
|
+
@db.typecast_value(:"#{x}_array", o).should equal(o)
|
184
|
+
@db.typecast_value(:"#{x}_array", a).should == a
|
185
|
+
@db.typecast_value(:"#{x}_array", a).should be_a_kind_of(eval("Sequel::Postgres::PG#{x.capitalize}Array"))
|
186
|
+
@db.typecast_value(:"#{x}_array", '{}').should == []
|
187
|
+
@db.typecast_value(:"#{x}_array", '{}').should be_a_kind_of(eval("Sequel::Postgres::PG#{x.capitalize}Array"))
|
188
|
+
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
|
+
proc{@db.typecast_value(:integer_array, {})}.should raise_error(Sequel::InvalidValue)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|