sequel 3.33.0 → 3.34.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 +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
|