epugh-sequel 0.0.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/README.rdoc +652 -0
- data/VERSION.yml +4 -0
- data/bin/sequel +104 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +85 -0
- data/lib/sequel/adapters/db2.rb +132 -0
- data/lib/sequel/adapters/dbi.rb +101 -0
- data/lib/sequel/adapters/do.rb +197 -0
- data/lib/sequel/adapters/do/mysql.rb +38 -0
- data/lib/sequel/adapters/do/postgres.rb +92 -0
- data/lib/sequel/adapters/do/sqlite.rb +31 -0
- data/lib/sequel/adapters/firebird.rb +307 -0
- data/lib/sequel/adapters/informix.rb +75 -0
- data/lib/sequel/adapters/jdbc.rb +485 -0
- data/lib/sequel/adapters/jdbc/h2.rb +62 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
- data/lib/sequel/adapters/mysql.rb +370 -0
- data/lib/sequel/adapters/odbc.rb +184 -0
- data/lib/sequel/adapters/openbase.rb +57 -0
- data/lib/sequel/adapters/oracle.rb +140 -0
- data/lib/sequel/adapters/postgres.rb +453 -0
- data/lib/sequel/adapters/shared/mssql.rb +93 -0
- data/lib/sequel/adapters/shared/mysql.rb +341 -0
- data/lib/sequel/adapters/shared/oracle.rb +62 -0
- data/lib/sequel/adapters/shared/postgres.rb +743 -0
- data/lib/sequel/adapters/shared/progress.rb +34 -0
- data/lib/sequel/adapters/shared/sqlite.rb +263 -0
- data/lib/sequel/adapters/sqlite.rb +243 -0
- data/lib/sequel/adapters/utils/date_format.rb +21 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
- data/lib/sequel/adapters/utils/unsupported.rb +62 -0
- data/lib/sequel/connection_pool.rb +258 -0
- data/lib/sequel/core.rb +204 -0
- data/lib/sequel/core_sql.rb +185 -0
- data/lib/sequel/database.rb +687 -0
- data/lib/sequel/database/schema_generator.rb +324 -0
- data/lib/sequel/database/schema_methods.rb +164 -0
- data/lib/sequel/database/schema_sql.rb +324 -0
- data/lib/sequel/dataset.rb +422 -0
- data/lib/sequel/dataset/convenience.rb +237 -0
- data/lib/sequel/dataset/prepared_statements.rb +220 -0
- data/lib/sequel/dataset/sql.rb +1105 -0
- data/lib/sequel/deprecated.rb +529 -0
- data/lib/sequel/exceptions.rb +44 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/sequel/extensions/inflector.rb +288 -0
- data/lib/sequel/extensions/pagination.rb +96 -0
- data/lib/sequel/extensions/pretty_table.rb +78 -0
- data/lib/sequel/extensions/query.rb +48 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +44 -0
- data/lib/sequel/migration.rb +212 -0
- data/lib/sequel/model.rb +142 -0
- data/lib/sequel/model/association_reflection.rb +263 -0
- data/lib/sequel/model/associations.rb +1024 -0
- data/lib/sequel/model/base.rb +911 -0
- data/lib/sequel/model/deprecated.rb +188 -0
- data/lib/sequel/model/deprecated_hooks.rb +103 -0
- data/lib/sequel/model/deprecated_inflector.rb +335 -0
- data/lib/sequel/model/deprecated_validations.rb +384 -0
- data/lib/sequel/model/errors.rb +37 -0
- data/lib/sequel/model/exceptions.rb +7 -0
- data/lib/sequel/model/inflections.rb +230 -0
- data/lib/sequel/model/plugins.rb +74 -0
- data/lib/sequel/object_graph.rb +230 -0
- data/lib/sequel/plugins/caching.rb +122 -0
- data/lib/sequel/plugins/hook_class_methods.rb +122 -0
- data/lib/sequel/plugins/schema.rb +53 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +373 -0
- data/lib/sequel/sql.rb +854 -0
- data/lib/sequel/version.rb +11 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/ado_spec.rb +46 -0
- data/spec/adapters/firebird_spec.rb +376 -0
- data/spec/adapters/informix_spec.rb +96 -0
- data/spec/adapters/mysql_spec.rb +875 -0
- data/spec/adapters/oracle_spec.rb +272 -0
- data/spec/adapters/postgres_spec.rb +692 -0
- data/spec/adapters/spec_helper.rb +10 -0
- data/spec/adapters/sqlite_spec.rb +550 -0
- data/spec/core/connection_pool_spec.rb +526 -0
- data/spec/core/core_ext_spec.rb +156 -0
- data/spec/core/core_sql_spec.rb +528 -0
- data/spec/core/database_spec.rb +1214 -0
- data/spec/core/dataset_spec.rb +3513 -0
- data/spec/core/expression_filters_spec.rb +363 -0
- data/spec/core/migration_spec.rb +261 -0
- data/spec/core/object_graph_spec.rb +280 -0
- data/spec/core/pretty_table_spec.rb +58 -0
- data/spec/core/schema_generator_spec.rb +167 -0
- data/spec/core/schema_spec.rb +778 -0
- data/spec/core/spec_helper.rb +82 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/inflector_spec.rb +122 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/schema_spec.rb +111 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/extensions/spec_helper.rb +90 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/validation_class_methods_spec.rb +1054 -0
- data/spec/integration/dataset_test.rb +160 -0
- data/spec/integration/eager_loader_test.rb +683 -0
- data/spec/integration/prepared_statement_test.rb +130 -0
- data/spec/integration/schema_test.rb +183 -0
- data/spec/integration/spec_helper.rb +75 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +93 -0
- data/spec/model/associations_spec.rb +1780 -0
- data/spec/model/base_spec.rb +494 -0
- data/spec/model/caching_spec.rb +217 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1165 -0
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/model/model_spec.rb +588 -0
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/model/record_spec.rb +1243 -0
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +202 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
|
2
|
+
|
|
3
|
+
unless defined?(ORACLE_DB)
|
|
4
|
+
ORACLE_DB = Sequel.connect('oracle://hr:hr@localhost/XE')
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
if ORACLE_DB.table_exists?(:items)
|
|
8
|
+
ORACLE_DB.drop_table :items
|
|
9
|
+
end
|
|
10
|
+
ORACLE_DB.create_table :items do
|
|
11
|
+
varchar2 :name, :size => 50
|
|
12
|
+
number :value, :size => 38
|
|
13
|
+
date :date_created
|
|
14
|
+
index :value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if ORACLE_DB.table_exists?(:books)
|
|
18
|
+
ORACLE_DB.drop_table :books
|
|
19
|
+
end
|
|
20
|
+
ORACLE_DB.create_table :books do
|
|
21
|
+
number :id, :size => 38
|
|
22
|
+
varchar2 :title, :size => 50
|
|
23
|
+
number :category_id, :size => 38
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if ORACLE_DB.table_exists?(:categories)
|
|
27
|
+
ORACLE_DB.drop_table :categories
|
|
28
|
+
end
|
|
29
|
+
ORACLE_DB.create_table :categories do
|
|
30
|
+
number :id, :size => 38
|
|
31
|
+
varchar2 :cat_name, :size => 50
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context "An Oracle database" do
|
|
35
|
+
specify "should provide disconnect functionality" do
|
|
36
|
+
ORACLE_DB.execute("select user from dual")
|
|
37
|
+
ORACLE_DB.pool.size.should == 1
|
|
38
|
+
ORACLE_DB.disconnect
|
|
39
|
+
ORACLE_DB.pool.size.should == 0
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
specify "should provide schema information" do
|
|
43
|
+
books_schema = [
|
|
44
|
+
[:id, {:char_size=>0, :type=>:number, :allow_null=>true, :type_string=>"NUMBER(38)", :data_size=>22, :precision=>38, :char_used=>false, :scale=>0, :charset_form=>nil, :fsprecision=>38, :lfprecision=>0, :db_type=>"NUMBER(38)"}],
|
|
45
|
+
[:title, {:char_size=>50, :type=>:varchar2, :allow_null=>true, :type_string=>"VARCHAR2(50)", :data_size=>50, :precision=>0, :char_used=>false, :scale=>0, :charset_form=>:implicit, :fsprecision=>0, :lfprecision=>0, :db_type=>"VARCHAR2(50)"}],
|
|
46
|
+
[:category_id, {:char_size=>0, :type=>:number, :allow_null=>true, :type_string=>"NUMBER(38)", :data_size=>22, :precision=>38, :char_used=>false, :scale=>0, :charset_form=>nil, :fsprecision=>38, :lfprecision=>0, :db_type=>"NUMBER(38)"}]]
|
|
47
|
+
categories_schema = [
|
|
48
|
+
[:id, {:char_size=>0, :type=>:number, :allow_null=>true, :type_string=>"NUMBER(38)", :data_size=>22, :precision=>38, :char_used=>false, :scale=>0, :charset_form=>nil, :fsprecision=>38, :lfprecision=>0, :db_type=>"NUMBER(38)"}],
|
|
49
|
+
[:cat_name, {:char_size=>50, :type=>:varchar2, :allow_null=>true, :type_string=>"VARCHAR2(50)", :data_size=>50, :precision=>0, :char_used=>false, :scale=>0, :charset_form=>:implicit, :fsprecision=>0, :lfprecision=>0, :db_type=>"VARCHAR2(50)"}]]
|
|
50
|
+
items_schema = [
|
|
51
|
+
[:name, {:char_size=>50, :type=>:varchar2, :allow_null=>true, :type_string=>"VARCHAR2(50)", :data_size=>50, :precision=>0, :char_used=>false, :scale=>0, :charset_form=>:implicit, :fsprecision=>0, :lfprecision=>0, :db_type=>"VARCHAR2(50)"}],
|
|
52
|
+
[:value, {:char_size=>0, :type=>:number, :allow_null=>true, :type_string=>"NUMBER(38)", :data_size=>22, :precision=>38, :char_used=>false, :scale=>0, :charset_form=>nil, :fsprecision=>38, :lfprecision=>0, :db_type=>"NUMBER(38)"}],
|
|
53
|
+
[:date_created, {:charset_form=>nil, :type=>:date, :type_string=>"DATE", :fsprecision=>0, :data_size=>7, :lfprecision=>0, :precision=>0, :db_type=>"DATE", :char_used=>false, :char_size=>0, :scale=>0, :allow_null=>true}]]
|
|
54
|
+
|
|
55
|
+
{:books => books_schema, :categories => categories_schema, :items => items_schema}.each_pair do |table, expected_schema|
|
|
56
|
+
schema = ORACLE_DB.schema(table)
|
|
57
|
+
schema.should_not be_nil
|
|
58
|
+
expected_schema.should == schema
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context "An Oracle dataset" do
|
|
64
|
+
setup do
|
|
65
|
+
@d = ORACLE_DB[:items]
|
|
66
|
+
@d.delete # remove all records
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
specify "should return the correct record count" do
|
|
70
|
+
@d.count.should == 0
|
|
71
|
+
@d << {:name => 'abc', :value => 123}
|
|
72
|
+
@d << {:name => 'abc', :value => 456}
|
|
73
|
+
@d << {:name => 'def', :value => 789}
|
|
74
|
+
@d.count.should == 3
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
specify "should return the correct records" do
|
|
78
|
+
@d.to_a.should == []
|
|
79
|
+
@d << {:name => 'abc', :value => 123}
|
|
80
|
+
@d << {:name => 'abc', :value => 456}
|
|
81
|
+
@d << {:name => 'def', :value => 789}
|
|
82
|
+
|
|
83
|
+
@d.order(:value).to_a.should == [
|
|
84
|
+
{:date_created=>nil, :name => 'abc', :value => 123},
|
|
85
|
+
{:date_created=>nil, :name => 'abc', :value => 456},
|
|
86
|
+
{:date_created=>nil, :name => 'def', :value => 789}
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
@d.select(:name).uniq.order_by(:name).to_a.should == [
|
|
90
|
+
{:name => 'abc'},
|
|
91
|
+
{:name => 'def'}
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
@d.order(:value.desc).limit(1).to_a.should == [
|
|
95
|
+
{:date_created=>nil, :name => 'def', :value => 789}
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
@d.filter(:name => 'abc').to_a.should == [
|
|
99
|
+
{:date_created=>nil, :name => 'abc', :value => 123},
|
|
100
|
+
{:date_created=>nil, :name => 'abc', :value => 456}
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
@d.order(:value.desc).filter(:name => 'abc').to_a.should == [
|
|
104
|
+
{:date_created=>nil, :name => 'abc', :value => 456},
|
|
105
|
+
{:date_created=>nil, :name => 'abc', :value => 123}
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
@d.filter(:name => 'abc').limit(1).to_a.should == [
|
|
109
|
+
{:date_created=>nil, :name => 'abc', :value => 123}
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
@d.filter(:name => 'abc').order(:value.desc).limit(1).to_a.should == [
|
|
113
|
+
{:date_created=>nil, :name => 'abc', :value => 456}
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
@d.filter(:name => 'abc').order(:value).limit(1).to_a.should == [
|
|
117
|
+
{:date_created=>nil, :name => 'abc', :value => 123}
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
@d.order(:value).limit(1).to_a.should == [
|
|
121
|
+
{:date_created=>nil, :name => 'abc', :value => 123}
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
@d.order(:value).limit(1, 1).to_a.should == [
|
|
125
|
+
{:date_created=>nil, :name => 'abc', :value => 456}
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
@d.order(:value).limit(1, 2).to_a.should == [
|
|
129
|
+
{:date_created=>nil, :name => 'def', :value => 789}
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
@d.avg(:value).to_i.should == (789+123+456)/3
|
|
133
|
+
|
|
134
|
+
@d.max(:value).to_i.should == 789
|
|
135
|
+
|
|
136
|
+
@d.select(:name, :AVG.sql_function(:value)).filter(:name => 'abc').group(:name).to_a.should == [
|
|
137
|
+
{:name => 'abc', :"avg(value)" => (456+123)/2.0}
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
@d.select(:AVG.sql_function(:value)).group(:name).order(:name).limit(1).to_a.should == [
|
|
141
|
+
{:"avg(value)" => (456+123)/2.0}
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
@d.select(:name, :AVG.sql_function(:value)).group(:name).order(:name).to_a.should == [
|
|
145
|
+
{:name => 'abc', :"avg(value)" => (456+123)/2.0},
|
|
146
|
+
{:name => 'def', :"avg(value)" => 789*1.0}
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
@d.select(:name, :AVG.sql_function(:value)).group(:name).order(:name).to_a.should == [
|
|
150
|
+
{:name => 'abc', :"avg(value)" => (456+123)/2.0},
|
|
151
|
+
{:name => 'def', :"avg(value)" => 789*1.0}
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
@d.select(:name, :AVG.sql_function(:value)).group(:name).having(:name => ['abc', 'def']).order(:name).to_a.should == [
|
|
155
|
+
{:name => 'abc', :"avg(value)" => (456+123)/2.0},
|
|
156
|
+
{:name => 'def', :"avg(value)" => 789*1.0}
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
@d.select(:name, :value).filter(:name => 'abc').union(@d.select(:name, :value).filter(:name => 'def')).order(:value).to_a.should == [
|
|
160
|
+
{:name => 'abc', :value => 123},
|
|
161
|
+
{:name => 'abc', :value => 456},
|
|
162
|
+
{:name => 'def', :value => 789}
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
specify "should update records correctly" do
|
|
168
|
+
@d << {:name => 'abc', :value => 123}
|
|
169
|
+
@d << {:name => 'abc', :value => 456}
|
|
170
|
+
@d << {:name => 'def', :value => 789}
|
|
171
|
+
@d.filter(:name => 'abc').update(:value => 530)
|
|
172
|
+
|
|
173
|
+
# the third record should stay the same
|
|
174
|
+
# floating-point precision bullshit
|
|
175
|
+
@d[:name => 'def'][:value].should == 789
|
|
176
|
+
@d.filter(:value => 530).count.should == 2
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
specify "should translate values correctly" do
|
|
180
|
+
@d << {:name => 'abc', :value => 456}
|
|
181
|
+
@d << {:name => 'def', :value => 789}
|
|
182
|
+
@d.filter(:value > 500).update(:date_created => "to_timestamp('2009-09-09', 'YYYY-MM-DD')".lit)
|
|
183
|
+
|
|
184
|
+
@d[:name => 'def'][:date_created].should == Time.parse('2009-09-09')
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
specify "should delete records correctly" do
|
|
188
|
+
@d << {:name => 'abc', :value => 123}
|
|
189
|
+
@d << {:name => 'abc', :value => 456}
|
|
190
|
+
@d << {:name => 'def', :value => 789}
|
|
191
|
+
@d.filter(:name => 'abc').delete
|
|
192
|
+
|
|
193
|
+
@d.count.should == 1
|
|
194
|
+
@d.first[:name].should == 'def'
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
specify "should be able to literalize booleans" do
|
|
198
|
+
proc {@d.literal(true)}.should_not raise_error
|
|
199
|
+
proc {@d.literal(false)}.should_not raise_error
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
specify "should support transactions" do
|
|
203
|
+
ORACLE_DB.transaction do
|
|
204
|
+
@d << {:name => 'abc', :value => 1}
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
@d.count.should == 1
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
context "Joined Oracle dataset" do
|
|
212
|
+
setup do
|
|
213
|
+
@d1 = ORACLE_DB[:books]
|
|
214
|
+
@d1.delete # remove all records
|
|
215
|
+
@d1 << {:id => 1, :title => 'aaa', :category_id => 100}
|
|
216
|
+
@d1 << {:id => 2, :title => 'bbb', :category_id => 100}
|
|
217
|
+
@d1 << {:id => 3, :title => 'ccc', :category_id => 101}
|
|
218
|
+
@d1 << {:id => 4, :title => 'ddd', :category_id => 102}
|
|
219
|
+
|
|
220
|
+
@d2 = ORACLE_DB[:categories]
|
|
221
|
+
@d2.delete # remove all records
|
|
222
|
+
@d2 << {:id => 100, :cat_name => 'ruby'}
|
|
223
|
+
@d2 << {:id => 101, :cat_name => 'rails'}
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
specify "should return correct result" do
|
|
227
|
+
@d1.join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id).to_a.should == [
|
|
228
|
+
{:id => 1, :title => 'aaa', :cat_name => 'ruby'},
|
|
229
|
+
{:id => 2, :title => 'bbb', :cat_name => 'ruby'},
|
|
230
|
+
{:id => 3, :title => 'ccc', :cat_name => 'rails'}
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
@d1.join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id).limit(2, 1).to_a.should == [
|
|
234
|
+
{:id => 2, :title => 'bbb', :cat_name => 'ruby'},
|
|
235
|
+
{:id => 3, :title => 'ccc', :cat_name => 'rails'},
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
@d1.left_outer_join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id).to_a.should == [
|
|
239
|
+
{:id => 1, :title => 'aaa', :cat_name => 'ruby'},
|
|
240
|
+
{:id => 2, :title => 'bbb', :cat_name => 'ruby'},
|
|
241
|
+
{:id => 3, :title => 'ccc', :cat_name => 'rails'},
|
|
242
|
+
{:id => 4, :title => 'ddd', :cat_name => nil}
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
@d1.left_outer_join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id.desc).limit(2, 0).to_a.should == [
|
|
246
|
+
{:id => 4, :title => 'ddd', :cat_name => nil},
|
|
247
|
+
{:id => 3, :title => 'ccc', :cat_name => 'rails'}
|
|
248
|
+
]
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
context "Oracle aliasing" do
|
|
253
|
+
setup do
|
|
254
|
+
@d1 = ORACLE_DB[:books]
|
|
255
|
+
@d1.delete # remove all records
|
|
256
|
+
@d1 << {:id => 1, :title => 'aaa', :category_id => 100}
|
|
257
|
+
@d1 << {:id => 2, :title => 'bbb', :category_id => 100}
|
|
258
|
+
@d1 << {:id => 3, :title => 'bbb', :category_id => 100}
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
specify "should allow columns to be renamed" do
|
|
262
|
+
@d1.select(:title.as(:name)).order_by(:id).to_a.should == [
|
|
263
|
+
{ :name => 'aaa' },
|
|
264
|
+
{ :name => 'bbb' },
|
|
265
|
+
{ :name => 'bbb' },
|
|
266
|
+
]
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
specify "nested queries should work" do
|
|
270
|
+
@d1.select(:title).group_by(:title).count.should == 2
|
|
271
|
+
end
|
|
272
|
+
end
|
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
|
2
|
+
|
|
3
|
+
unless defined?(POSTGRES_DB)
|
|
4
|
+
POSTGRES_URL = 'postgres://postgres:postgres@localhost:5432/reality_spec' unless defined? POSTGRES_URL
|
|
5
|
+
POSTGRES_DB = Sequel.connect(ENV['SEQUEL_PG_SPEC_DB']||POSTGRES_URL)
|
|
6
|
+
end
|
|
7
|
+
#POSTGRES_DB.instance_variable_set(:@server_version, 80100)
|
|
8
|
+
POSTGRES_DB.create_table! :test do
|
|
9
|
+
text :name
|
|
10
|
+
integer :value, :index => true
|
|
11
|
+
end
|
|
12
|
+
POSTGRES_DB.create_table! :test2 do
|
|
13
|
+
text :name
|
|
14
|
+
integer :value
|
|
15
|
+
end
|
|
16
|
+
POSTGRES_DB.create_table! :test3 do
|
|
17
|
+
integer :value
|
|
18
|
+
timestamp :time
|
|
19
|
+
end
|
|
20
|
+
POSTGRES_DB.create_table! :test4 do
|
|
21
|
+
varchar :name, :size => 20
|
|
22
|
+
bytea :value
|
|
23
|
+
end
|
|
24
|
+
POSTGRES_DB.create_table! :test5 do
|
|
25
|
+
primary_key :xid
|
|
26
|
+
integer :value
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
context "A PostgreSQL database" do
|
|
30
|
+
setup do
|
|
31
|
+
@db = POSTGRES_DB
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
specify "should provide disconnect functionality" do
|
|
35
|
+
@db.tables
|
|
36
|
+
@db.pool.size.should == 1
|
|
37
|
+
@db.disconnect
|
|
38
|
+
@db.pool.size.should == 0
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
specify "should provide the server version" do
|
|
42
|
+
@db.server_version.should > 70000
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
specify "should raise Sequel::Error on error" do
|
|
46
|
+
proc{@db << "SELECT 1 + 'a'"}.should raise_error(Sequel::Error)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
specify "should correctly parse the schema" do
|
|
50
|
+
require 'logger'
|
|
51
|
+
@db.schema(:test3, :reload=>true).should == [
|
|
52
|
+
[:value, {:type=>:integer, :allow_null=>true, :default=>nil, :db_type=>"integer", :primary_key=>false}],
|
|
53
|
+
[:time, {:type=>:datetime, :allow_null=>true, :default=>nil, :db_type=>"timestamp without time zone", :primary_key=>false}]
|
|
54
|
+
]
|
|
55
|
+
@db.schema(:test4, :reload=>true).should == [
|
|
56
|
+
[:name, {:type=>:string, :allow_null=>true, :default=>nil, :db_type=>"character varying(20)", :primary_key=>false}],
|
|
57
|
+
[:value, {:type=>:blob, :allow_null=>true, :default=>nil, :db_type=>"bytea", :primary_key=>false}]
|
|
58
|
+
]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "A PostgreSQL dataset" do
|
|
63
|
+
setup do
|
|
64
|
+
@d = POSTGRES_DB[:test]
|
|
65
|
+
@d.delete # remove all records
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
specify "should return the correct record count" do
|
|
69
|
+
@d.count.should == 0
|
|
70
|
+
@d << {:name => 'abc', :value => 123}
|
|
71
|
+
@d << {:name => 'abc', :value => 456}
|
|
72
|
+
@d << {:name => 'def', :value => 789}
|
|
73
|
+
@d.count.should == 3
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
specify "should return the correct records" do
|
|
77
|
+
@d.to_a.should == []
|
|
78
|
+
@d << {:name => 'abc', :value => 123}
|
|
79
|
+
@d << {:name => 'abc', :value => 456}
|
|
80
|
+
@d << {:name => 'def', :value => 789}
|
|
81
|
+
|
|
82
|
+
@d.order(:value).to_a.should == [
|
|
83
|
+
{:name => 'abc', :value => 123},
|
|
84
|
+
{:name => 'abc', :value => 456},
|
|
85
|
+
{:name => 'def', :value => 789}
|
|
86
|
+
]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
specify "should update records correctly" do
|
|
90
|
+
@d << {:name => 'abc', :value => 123}
|
|
91
|
+
@d << {:name => 'abc', :value => 456}
|
|
92
|
+
@d << {:name => 'def', :value => 789}
|
|
93
|
+
@d.filter(:name => 'abc').update(:value => 530)
|
|
94
|
+
|
|
95
|
+
# the third record should stay the same
|
|
96
|
+
# floating-point precision bullshit
|
|
97
|
+
@d[:name => 'def'][:value].should == 789
|
|
98
|
+
@d.filter(:value => 530).count.should == 2
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
specify "should delete records correctly" do
|
|
102
|
+
@d << {:name => 'abc', :value => 123}
|
|
103
|
+
@d << {:name => 'abc', :value => 456}
|
|
104
|
+
@d << {:name => 'def', :value => 789}
|
|
105
|
+
@d.filter(:name => 'abc').delete
|
|
106
|
+
|
|
107
|
+
@d.count.should == 1
|
|
108
|
+
@d.first[:name].should == 'def'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
specify "should be able to literalize booleans" do
|
|
112
|
+
proc {@d.literal(true)}.should_not raise_error
|
|
113
|
+
proc {@d.literal(false)}.should_not raise_error
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
specify "should quote columns and tables using double quotes if quoting identifiers" do
|
|
117
|
+
@d.quote_identifiers = true
|
|
118
|
+
@d.select(:name).sql.should == \
|
|
119
|
+
'SELECT "name" FROM "test"'
|
|
120
|
+
|
|
121
|
+
@d.select('COUNT(*)'.lit).sql.should == \
|
|
122
|
+
'SELECT COUNT(*) FROM "test"'
|
|
123
|
+
|
|
124
|
+
@d.select(:max.sql_function(:value)).sql.should == \
|
|
125
|
+
'SELECT max("value") FROM "test"'
|
|
126
|
+
|
|
127
|
+
@d.select(:NOW.sql_function).sql.should == \
|
|
128
|
+
'SELECT NOW() FROM "test"'
|
|
129
|
+
|
|
130
|
+
@d.select(:max.sql_function(:items__value)).sql.should == \
|
|
131
|
+
'SELECT max("items"."value") FROM "test"'
|
|
132
|
+
|
|
133
|
+
@d.order(:name.desc).sql.should == \
|
|
134
|
+
'SELECT * FROM "test" ORDER BY "name" DESC'
|
|
135
|
+
|
|
136
|
+
@d.select('test.name AS item_name'.lit).sql.should == \
|
|
137
|
+
'SELECT test.name AS item_name FROM "test"'
|
|
138
|
+
|
|
139
|
+
@d.select('"name"'.lit).sql.should == \
|
|
140
|
+
'SELECT "name" FROM "test"'
|
|
141
|
+
|
|
142
|
+
@d.select('max(test."name") AS "max_name"'.lit).sql.should == \
|
|
143
|
+
'SELECT max(test."name") AS "max_name" FROM "test"'
|
|
144
|
+
|
|
145
|
+
@d.select(:test.sql_function(:abc, 'hello')).sql.should == \
|
|
146
|
+
"SELECT test(\"abc\", 'hello') FROM \"test\""
|
|
147
|
+
|
|
148
|
+
@d.select(:test.sql_function(:abc__def, 'hello')).sql.should == \
|
|
149
|
+
"SELECT test(\"abc\".\"def\", 'hello') FROM \"test\""
|
|
150
|
+
|
|
151
|
+
@d.select(:test.sql_function(:abc__def, 'hello').as(:x2)).sql.should == \
|
|
152
|
+
"SELECT test(\"abc\".\"def\", 'hello') AS \"x2\" FROM \"test\""
|
|
153
|
+
|
|
154
|
+
@d.insert_sql(:value => 333).should =~ \
|
|
155
|
+
/\AINSERT INTO "test" \("value"\) VALUES \(333\)( RETURNING NULL)?\z/
|
|
156
|
+
|
|
157
|
+
@d.insert_sql(:x => :y).should =~ \
|
|
158
|
+
/\AINSERT INTO "test" \("x"\) VALUES \("y"\)( RETURNING NULL)?\z/
|
|
159
|
+
|
|
160
|
+
@d.disable_insert_returning.insert_sql(:value => 333).should =~ \
|
|
161
|
+
/\AINSERT INTO "test" \("value"\) VALUES \(333\)\z/
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
specify "should quote fields correctly when reversing the order if quoting identifiers" do
|
|
165
|
+
@d.quote_identifiers = true
|
|
166
|
+
@d.reverse_order(:name).sql.should == \
|
|
167
|
+
'SELECT * FROM "test" ORDER BY "name" DESC'
|
|
168
|
+
|
|
169
|
+
@d.reverse_order(:name.desc).sql.should == \
|
|
170
|
+
'SELECT * FROM "test" ORDER BY "name" ASC'
|
|
171
|
+
|
|
172
|
+
@d.reverse_order(:name, :test.desc).sql.should == \
|
|
173
|
+
'SELECT * FROM "test" ORDER BY "name" DESC, "test" ASC'
|
|
174
|
+
|
|
175
|
+
@d.reverse_order(:name.desc, :test).sql.should == \
|
|
176
|
+
'SELECT * FROM "test" ORDER BY "name" ASC, "test" DESC'
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
specify "should support transactions" do
|
|
180
|
+
POSTGRES_DB.transaction do
|
|
181
|
+
@d << {:name => 'abc', :value => 1}
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
@d.count.should == 1
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
specify "should have #transaction yield the connection" do
|
|
188
|
+
POSTGRES_DB.transaction do |conn|
|
|
189
|
+
conn.should_not == nil
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
specify "should correctly rollback transactions" do
|
|
194
|
+
proc do
|
|
195
|
+
POSTGRES_DB.transaction do
|
|
196
|
+
@d << {:name => 'abc', :value => 1}
|
|
197
|
+
raise Interrupt, 'asdf'
|
|
198
|
+
end
|
|
199
|
+
end.should raise_error(Interrupt)
|
|
200
|
+
|
|
201
|
+
@d.count.should == 0
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
specify "should handle returning inside of the block by committing" do
|
|
205
|
+
def POSTGRES_DB.ret_commit
|
|
206
|
+
transaction do
|
|
207
|
+
self[:test] << {:name => 'abc'}
|
|
208
|
+
return
|
|
209
|
+
self[:test] << {:name => 'd'}
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
@d.count.should == 0
|
|
213
|
+
POSTGRES_DB.ret_commit
|
|
214
|
+
@d.count.should == 1
|
|
215
|
+
POSTGRES_DB.ret_commit
|
|
216
|
+
@d.count.should == 2
|
|
217
|
+
proc do
|
|
218
|
+
POSTGRES_DB.transaction do
|
|
219
|
+
raise Interrupt, 'asdf'
|
|
220
|
+
end
|
|
221
|
+
end.should raise_error(Interrupt)
|
|
222
|
+
|
|
223
|
+
@d.count.should == 2
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
specify "should support nested transactions through savepoints using the savepoint option" do
|
|
227
|
+
POSTGRES_DB.transaction do
|
|
228
|
+
@d << {:name => '1'}
|
|
229
|
+
POSTGRES_DB.transaction(:savepoint=>true) do
|
|
230
|
+
@d << {:name => '2'}
|
|
231
|
+
POSTGRES_DB.transaction do
|
|
232
|
+
@d << {:name => '3'}
|
|
233
|
+
raise Sequel::Error::Rollback
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
@d << {:name => '4'}
|
|
237
|
+
POSTGRES_DB.transaction do
|
|
238
|
+
@d << {:name => '6'}
|
|
239
|
+
POSTGRES_DB.transaction(:savepoint=>true) do
|
|
240
|
+
@d << {:name => '7'}
|
|
241
|
+
raise Sequel::Error::Rollback
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
@d << {:name => '5'}
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
@d.order(:name).map(:name).should == %w{1 4 5 6}
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
specify "should support regexps" do
|
|
251
|
+
@d << {:name => 'abc', :value => 1}
|
|
252
|
+
@d << {:name => 'bcd', :value => 2}
|
|
253
|
+
@d.filter(:name => /bc/).count.should == 2
|
|
254
|
+
@d.filter(:name => /^bc/).count.should == 1
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
specify "should correctly escape strings" do
|
|
258
|
+
POSTGRES_DB['SELECT ? AS a', "\\dingo"].get(:a) == "\\dingo"
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
specify "should correctly escape strings with quotes" do
|
|
262
|
+
POSTGRES_DB['SELECT ? AS a', "\\'dingo"].get(:a) == "\\'dingo"
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
specify "should properly escape binary data" do
|
|
266
|
+
POSTGRES_DB['SELECT ? AS a', "\1\2\3".to_sequel_blob].get(:a) == "\1\2\3"
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
specify "should retrieve binary data as Blob object" do
|
|
270
|
+
d = POSTGRES_DB[:test4]
|
|
271
|
+
d << {:name => '123', :value => "\1\2\3".to_sequel_blob}
|
|
272
|
+
retrieved_binary_value = d[:name => '123'][:value]
|
|
273
|
+
retrieved_binary_value.should be_a_kind_of(::Sequel::SQL::Blob)
|
|
274
|
+
retrieved_binary_value.should == "\1\2\3"
|
|
275
|
+
retrieved_binary_value = d[:value => "\1\2\3".to_sequel_blob][:value]
|
|
276
|
+
retrieved_binary_value.should be_a_kind_of(::Sequel::SQL::Blob)
|
|
277
|
+
retrieved_binary_value.should == "\1\2\3"
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
specify "should properly receive binary data" do
|
|
281
|
+
POSTGRES_DB['SELECT ?::bytea AS a', "a"].get(:a) == "a"
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
context "A PostgreSQL dataset with a timestamp field" do
|
|
286
|
+
setup do
|
|
287
|
+
@d = POSTGRES_DB[:test3]
|
|
288
|
+
@d.delete
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
specify "should store milliseconds in time fields" do
|
|
292
|
+
t = Time.now
|
|
293
|
+
@d << {:value=>1, :time=>t}
|
|
294
|
+
@d.literal(@d[:value =>'1'][:time]).should == @d.literal(t)
|
|
295
|
+
@d[:value=>'1'][:time].usec.should == t.usec
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
context "A PostgreSQL database" do
|
|
300
|
+
setup do
|
|
301
|
+
@db = POSTGRES_DB
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
specify "should support column operations" do
|
|
305
|
+
@db.create_table!(:test2){text :name; integer :value}
|
|
306
|
+
@db[:test2] << {}
|
|
307
|
+
@db[:test2].columns.should == [:name, :value]
|
|
308
|
+
|
|
309
|
+
@db.add_column :test2, :xyz, :text, :default => '000'
|
|
310
|
+
@db[:test2].columns.should == [:name, :value, :xyz]
|
|
311
|
+
@db[:test2] << {:name => 'mmm', :value => 111}
|
|
312
|
+
@db[:test2].first[:xyz].should == '000'
|
|
313
|
+
|
|
314
|
+
@db[:test2].columns.should == [:name, :value, :xyz]
|
|
315
|
+
@db.drop_column :test2, :xyz
|
|
316
|
+
|
|
317
|
+
@db[:test2].columns.should == [:name, :value]
|
|
318
|
+
|
|
319
|
+
@db[:test2].delete
|
|
320
|
+
@db.add_column :test2, :xyz, :text, :default => '000'
|
|
321
|
+
@db[:test2] << {:name => 'mmm', :value => 111, :xyz => 'qqqq'}
|
|
322
|
+
|
|
323
|
+
@db[:test2].columns.should == [:name, :value, :xyz]
|
|
324
|
+
@db.rename_column :test2, :xyz, :zyx
|
|
325
|
+
@db[:test2].columns.should == [:name, :value, :zyx]
|
|
326
|
+
@db[:test2].first[:zyx].should == 'qqqq'
|
|
327
|
+
|
|
328
|
+
@db.add_column :test2, :xyz, :float
|
|
329
|
+
@db[:test2].delete
|
|
330
|
+
@db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
|
|
331
|
+
@db.set_column_type :test2, :xyz, :integer
|
|
332
|
+
|
|
333
|
+
@db[:test2].first[:xyz].should == 57
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
context "A PostgreSQL database" do
|
|
338
|
+
setup do
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
specify "should support fulltext indexes" do
|
|
342
|
+
g = Sequel::Schema::Generator.new(POSTGRES_DB) do
|
|
343
|
+
text :title
|
|
344
|
+
text :body
|
|
345
|
+
full_text_index [:title, :body]
|
|
346
|
+
end
|
|
347
|
+
POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
|
348
|
+
"CREATE TABLE posts (title text, body text)",
|
|
349
|
+
"CREATE INDEX posts_title_body_index ON posts USING gin (to_tsvector('simple', (COALESCE(title, '') || ' ' || COALESCE(body, ''))))"
|
|
350
|
+
]
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
specify "should support fulltext indexes with a specific language" do
|
|
354
|
+
g = Sequel::Schema::Generator.new(POSTGRES_DB) do
|
|
355
|
+
text :title
|
|
356
|
+
text :body
|
|
357
|
+
full_text_index [:title, :body], :language => 'french'
|
|
358
|
+
end
|
|
359
|
+
POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
|
360
|
+
"CREATE TABLE posts (title text, body text)",
|
|
361
|
+
"CREATE INDEX posts_title_body_index ON posts USING gin (to_tsvector('french', (COALESCE(title, '') || ' ' || COALESCE(body, ''))))"
|
|
362
|
+
]
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
specify "should support full_text_search" do
|
|
366
|
+
POSTGRES_DB[:posts].full_text_search(:title, 'ruby').sql.should ==
|
|
367
|
+
"SELECT * FROM posts WHERE (to_tsvector('simple', (COALESCE(title, ''))) @@ to_tsquery('simple', 'ruby'))"
|
|
368
|
+
|
|
369
|
+
POSTGRES_DB[:posts].full_text_search([:title, :body], ['ruby', 'sequel']).sql.should ==
|
|
370
|
+
"SELECT * FROM posts WHERE (to_tsvector('simple', (COALESCE(title, '') || ' ' || COALESCE(body, ''))) @@ to_tsquery('simple', 'ruby | sequel'))"
|
|
371
|
+
|
|
372
|
+
POSTGRES_DB[:posts].full_text_search(:title, 'ruby', :language => 'french').sql.should ==
|
|
373
|
+
"SELECT * FROM posts WHERE (to_tsvector('french', (COALESCE(title, ''))) @@ to_tsquery('french', 'ruby'))"
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
specify "should support spatial indexes" do
|
|
377
|
+
g = Sequel::Schema::Generator.new(POSTGRES_DB) do
|
|
378
|
+
geometry :geom
|
|
379
|
+
spatial_index [:geom]
|
|
380
|
+
end
|
|
381
|
+
POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
|
382
|
+
"CREATE TABLE posts (geom geometry)",
|
|
383
|
+
"CREATE INDEX posts_geom_index ON posts USING gist (geom)"
|
|
384
|
+
]
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
specify "should support indexes with index type" do
|
|
388
|
+
g = Sequel::Schema::Generator.new(POSTGRES_DB) do
|
|
389
|
+
varchar :title, :size => 5
|
|
390
|
+
index :title, :type => 'hash'
|
|
391
|
+
end
|
|
392
|
+
POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
|
393
|
+
"CREATE TABLE posts (title varchar(5))",
|
|
394
|
+
"CREATE INDEX posts_title_index ON posts USING hash (title)"
|
|
395
|
+
]
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
specify "should support unique indexes with index type" do
|
|
399
|
+
g = Sequel::Schema::Generator.new(POSTGRES_DB) do
|
|
400
|
+
varchar :title, :size => 5
|
|
401
|
+
index :title, :type => 'hash', :unique => true
|
|
402
|
+
end
|
|
403
|
+
POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
|
404
|
+
"CREATE TABLE posts (title varchar(5))",
|
|
405
|
+
"CREATE UNIQUE INDEX posts_title_index ON posts USING hash (title)"
|
|
406
|
+
]
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
specify "should support partial indexes" do
|
|
410
|
+
g = Sequel::Schema::Generator.new(POSTGRES_DB) do
|
|
411
|
+
varchar :title, :size => 5
|
|
412
|
+
index :title, :where => {:something => 5}
|
|
413
|
+
end
|
|
414
|
+
POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
|
415
|
+
"CREATE TABLE posts (title varchar(5))",
|
|
416
|
+
"CREATE INDEX posts_title_index ON posts (title) WHERE (something = 5)"
|
|
417
|
+
]
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
specify "should support identifiers for table names in indicies" do
|
|
421
|
+
g = Sequel::Schema::Generator.new(POSTGRES_DB) do
|
|
422
|
+
varchar :title, :size => 5
|
|
423
|
+
index :title, :where => {:something => 5}
|
|
424
|
+
end
|
|
425
|
+
POSTGRES_DB.create_table_sql_list(Sequel::SQL::Identifier.new(:posts__test), *g.create_info).should == [
|
|
426
|
+
"CREATE TABLE posts__test (title varchar(5))",
|
|
427
|
+
"CREATE INDEX posts__test_title_index ON posts__test (title) WHERE (something = 5)"
|
|
428
|
+
]
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
context "Postgres::Dataset#multi_insert_sql" do
|
|
433
|
+
setup do
|
|
434
|
+
@ds = POSTGRES_DB[:test]
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
specify "should return separate insert statements if server_version < 80200" do
|
|
438
|
+
@ds.meta_def(:server_version){80199}
|
|
439
|
+
|
|
440
|
+
@ds.multi_insert_sql([:x, :y], [[1, 2], [3, 4]]).should == [
|
|
441
|
+
'INSERT INTO test (x, y) VALUES (1, 2)',
|
|
442
|
+
'INSERT INTO test (x, y) VALUES (3, 4)'
|
|
443
|
+
]
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
specify "should a single insert statement if server_version >= 80200" do
|
|
447
|
+
@ds.meta_def(:server_version){80200}
|
|
448
|
+
|
|
449
|
+
@ds.multi_insert_sql([:x, :y], [[1, 2], [3, 4]]).should == [
|
|
450
|
+
'INSERT INTO test (x, y) VALUES (1, 2), (3, 4)'
|
|
451
|
+
]
|
|
452
|
+
|
|
453
|
+
@ds.meta_def(:server_version){80201}
|
|
454
|
+
|
|
455
|
+
@ds.multi_insert_sql([:x, :y], [[1, 2], [3, 4]]).should == [
|
|
456
|
+
'INSERT INTO test (x, y) VALUES (1, 2), (3, 4)'
|
|
457
|
+
]
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
context "Postgres::Dataset#insert" do
|
|
462
|
+
setup do
|
|
463
|
+
@ds = POSTGRES_DB[:test5]
|
|
464
|
+
@ds.delete
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
specify "should call insert_sql if server_version < 80200" do
|
|
468
|
+
@ds.meta_def(:server_version){80100}
|
|
469
|
+
@ds.should_receive(:execute_insert).once.with('INSERT INTO test5 (value) VALUES (10)', :table=>:test5, :values=>{:value=>10})
|
|
470
|
+
@ds.insert(:value=>10)
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
specify "should call insert_sql if disabling insert returning" do
|
|
474
|
+
@ds.disable_insert_returning!
|
|
475
|
+
@ds.should_receive(:execute_insert).once.with('INSERT INTO test5 (value) VALUES (10)', :table=>:test5, :values=>{:value=>10})
|
|
476
|
+
@ds.insert(:value=>10)
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
specify "should use INSERT RETURNING if server_version >= 80200" do
|
|
480
|
+
@ds.meta_def(:server_version){80201}
|
|
481
|
+
@ds.should_receive(:clone).once.with(:server=>:default, :sql=>'INSERT INTO test5 (value) VALUES (10) RETURNING xid').and_return(@ds)
|
|
482
|
+
@ds.should_receive(:single_value).once
|
|
483
|
+
@ds.insert(:value=>10)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
specify "should have insert_returning_sql use the RETURNING keyword" do
|
|
487
|
+
@ds.insert_returning_sql(:xid, :value=>10).should == "INSERT INTO test5 (value) VALUES (10) RETURNING xid"
|
|
488
|
+
@ds.insert_returning_sql('*'.lit, :value=>10).should == "INSERT INTO test5 (value) VALUES (10) RETURNING *"
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
specify "should have insert_select return nil if server_version < 80200" do
|
|
492
|
+
@ds.meta_def(:server_version){80100}
|
|
493
|
+
@ds.insert_select(:value=>10).should == nil
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
specify "should have insert_select insert the record and return the inserted record if server_version < 80200" do
|
|
497
|
+
@ds.meta_def(:server_version){80201}
|
|
498
|
+
h = @ds.insert_select(:value=>10)
|
|
499
|
+
h[:value].should == 10
|
|
500
|
+
@ds.first(:xid=>h[:xid])[:value].should == 10
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
specify "should correctly return the inserted record's primary key value" do
|
|
504
|
+
value1 = 10
|
|
505
|
+
id1 = @ds.insert(:value=>value1)
|
|
506
|
+
@ds.first(:xid=>id1)[:value].should == value1
|
|
507
|
+
value2 = 20
|
|
508
|
+
id2 = @ds.insert(:value=>value2)
|
|
509
|
+
@ds.first(:xid=>id2)[:value].should == value2
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
specify "should return nil if the table has no primary key" do
|
|
513
|
+
ds = POSTGRES_DB[:test4]
|
|
514
|
+
ds.delete
|
|
515
|
+
ds.insert(:name=>'a').should == nil
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
context "Postgres::Database schema qualified tables" do
|
|
520
|
+
setup do
|
|
521
|
+
POSTGRES_DB << "CREATE SCHEMA schema_test"
|
|
522
|
+
POSTGRES_DB.instance_variable_set(:@primary_keys, {})
|
|
523
|
+
POSTGRES_DB.instance_variable_set(:@primary_key_sequences, {})
|
|
524
|
+
end
|
|
525
|
+
teardown do
|
|
526
|
+
POSTGRES_DB << "DROP SCHEMA schema_test CASCADE"
|
|
527
|
+
POSTGRES_DB.default_schema = :public
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
specify "should be able to create, drop, select and insert into tables in a given schema" do
|
|
531
|
+
POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
|
|
532
|
+
POSTGRES_DB[:schema_test__schema_test].first.should == nil
|
|
533
|
+
POSTGRES_DB[:schema_test__schema_test].insert(:i=>1).should == 1
|
|
534
|
+
POSTGRES_DB[:schema_test__schema_test].first.should == {:i=>1}
|
|
535
|
+
POSTGRES_DB.from('schema_test.schema_test'.lit).first.should == {:i=>1}
|
|
536
|
+
POSTGRES_DB.drop_table(:schema_test__schema_test)
|
|
537
|
+
POSTGRES_DB.create_table(:schema_test.qualify(:schema_test)){integer :i}
|
|
538
|
+
POSTGRES_DB[:schema_test__schema_test].first.should == nil
|
|
539
|
+
POSTGRES_DB.from('schema_test.schema_test'.lit).first.should == nil
|
|
540
|
+
POSTGRES_DB.drop_table(:schema_test.qualify(:schema_test))
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
specify "#tables should include only tables in the public schema if no schema is given" do
|
|
544
|
+
POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
|
|
545
|
+
POSTGRES_DB.tables.should_not include(:schema_test)
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
specify "#tables should return tables in the schema provided by the :schema argument" do
|
|
549
|
+
POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
|
|
550
|
+
POSTGRES_DB.tables(:schema=>:schema_test).should == [:schema_test]
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
specify "#table_exists? should assume the public schema if no schema is provided" do
|
|
554
|
+
POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
|
|
555
|
+
POSTGRES_DB.table_exists?(:schema_test).should == false
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
specify "#table_exists? should see if the table is in a given schema" do
|
|
559
|
+
POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
|
|
560
|
+
POSTGRES_DB.table_exists?(:schema_test__schema_test).should == true
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
specify "should be able to get primary keys for tables in a given schema" do
|
|
564
|
+
POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
|
|
565
|
+
POSTGRES_DB.primary_key(:schema_test__schema_test).should == 'i'
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
specify "should be able to get serial sequences for tables in a given schema" do
|
|
569
|
+
POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
|
|
570
|
+
POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test"."schema_test_i_seq"'
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
specify "should be able to get custom sequences for tables in a given schema" do
|
|
574
|
+
POSTGRES_DB << "CREATE SEQUENCE schema_test.kseq"
|
|
575
|
+
POSTGRES_DB.create_table(:schema_test__schema_test){integer :j; primary_key :k, :type=>:integer, :default=>"nextval('schema_test.kseq'::regclass)".lit}
|
|
576
|
+
POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test"."kseq"'
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
specify "#default_schema= should change the default schema used from public" do
|
|
580
|
+
POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
|
|
581
|
+
POSTGRES_DB.default_schema = :schema_test
|
|
582
|
+
POSTGRES_DB.table_exists?(:schema_test).should == true
|
|
583
|
+
POSTGRES_DB.tables.should == [:schema_test]
|
|
584
|
+
POSTGRES_DB.primary_key(:schema_test__schema_test).should == 'i'
|
|
585
|
+
POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test"."schema_test_i_seq"'
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
if POSTGRES_DB.server_version >= 80300
|
|
590
|
+
|
|
591
|
+
POSTGRES_DB.create_table! :test6 do
|
|
592
|
+
text :title
|
|
593
|
+
text :body
|
|
594
|
+
full_text_index [:title, :body]
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
context "PostgreSQL tsearch2" do
|
|
598
|
+
before do
|
|
599
|
+
@ds = POSTGRES_DB[:test6]
|
|
600
|
+
end
|
|
601
|
+
after do
|
|
602
|
+
POSTGRES_DB[:test6].delete
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
specify "should search by indexed column" do
|
|
606
|
+
record = {:title => "oopsla conference", :body => "test"}
|
|
607
|
+
@ds << record
|
|
608
|
+
@ds.full_text_search(:title, "oopsla").all.should include(record)
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
specify "should join multiple coumns with spaces to search by last words in row" do
|
|
612
|
+
record = {:title => "multiple words", :body => "are easy to search"}
|
|
613
|
+
@ds << record
|
|
614
|
+
@ds.full_text_search([:title, :body], "words").all.should include(record)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
specify "should return rows with a NULL in one column if a match in another column" do
|
|
618
|
+
record = {:title => "multiple words", :body =>nil}
|
|
619
|
+
@ds << record
|
|
620
|
+
@ds.full_text_search([:title, :body], "words").all.should include(record)
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
context "Postgres::Database functions, languages, and triggers" do
|
|
626
|
+
setup do
|
|
627
|
+
@d = POSTGRES_DB
|
|
628
|
+
end
|
|
629
|
+
teardown do
|
|
630
|
+
@d.drop_function('tf', :if_exists=>true, :cascade=>true)
|
|
631
|
+
@d.drop_function('tf', :if_exists=>true, :cascade=>true, :args=>%w'integer integer')
|
|
632
|
+
@d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true)
|
|
633
|
+
@d.drop_trigger(:test5, :identity, :if_exists=>true, :cascade=>true)
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
specify "#create_function and #drop_function should create and drop functions" do
|
|
637
|
+
proc{@d['SELECT tf()'].all}.should raise_error(Sequel::DatabaseError)
|
|
638
|
+
args = ['tf', 'SELECT 1', {:returns=>:integer}]
|
|
639
|
+
@d.create_function_sql(*args).should =~ /\A\s*CREATE FUNCTION tf\(\)\s+RETURNS integer\s+LANGUAGE SQL\s+AS 'SELECT 1'\s*\z/
|
|
640
|
+
@d.create_function(*args)
|
|
641
|
+
rows = @d['SELECT tf()'].all.should == [{:tf=>1}]
|
|
642
|
+
@d.drop_function_sql('tf').should == 'DROP FUNCTION tf()'
|
|
643
|
+
@d.drop_function('tf')
|
|
644
|
+
proc{@d['SELECT tf()'].all}.should raise_error(Sequel::DatabaseError)
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
specify "#create_function and #drop_function should support options" do
|
|
648
|
+
args = ['tf', 'SELECT $1 + $2', {:args=>[[:integer, :a], :integer], :replace=>true, :returns=>:integer, :language=>'SQL', :behavior=>:immutable, :strict=>true, :security_definer=>true, :cost=>2, :set=>{:search_path => 'public'}}]
|
|
649
|
+
@d.create_function_sql(*args).should =~ /\A\s*CREATE OR REPLACE FUNCTION tf\(a integer, integer\)\s+RETURNS integer\s+LANGUAGE SQL\s+IMMUTABLE\s+STRICT\s+SECURITY DEFINER\s+COST 2\s+SET search_path = public\s+AS 'SELECT \$1 \+ \$2'\s*\z/
|
|
650
|
+
@d.create_function(*args)
|
|
651
|
+
# Make sure replace works
|
|
652
|
+
@d.create_function(*args)
|
|
653
|
+
rows = @d['SELECT tf(1, 2)'].all.should == [{:tf=>3}]
|
|
654
|
+
args = ['tf', {:if_exists=>true, :cascade=>true, :args=>[[:integer, :a], :integer]}]
|
|
655
|
+
@d.drop_function_sql(*args).should == 'DROP FUNCTION IF EXISTS tf(a integer, integer) CASCADE'
|
|
656
|
+
@d.drop_function(*args)
|
|
657
|
+
# Make sure if exists works
|
|
658
|
+
@d.drop_function(*args)
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
specify "#create_language and #drop_language should create and drop languages" do
|
|
662
|
+
@d.create_language_sql(:plpgsql).should == 'CREATE LANGUAGE plpgsql'
|
|
663
|
+
@d.create_language(:plpgsql)
|
|
664
|
+
proc{@d.create_language(:plpgsql)}.should raise_error(Sequel::DatabaseError)
|
|
665
|
+
@d.drop_language_sql(:plpgsql).should == 'DROP LANGUAGE plpgsql'
|
|
666
|
+
@d.drop_language(:plpgsql)
|
|
667
|
+
proc{@d.drop_language(:plpgsql)}.should raise_error(Sequel::DatabaseError)
|
|
668
|
+
@d.create_language_sql(:plpgsql, :trusted=>true, :handler=>:a, :validator=>:b).should == 'CREATE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b'
|
|
669
|
+
@d.drop_language_sql(:plpgsql, :if_exists=>true, :cascade=>true).should == 'DROP LANGUAGE IF EXISTS plpgsql CASCADE'
|
|
670
|
+
# Make sure if exists works
|
|
671
|
+
@d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true)
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
specify "#create_trigger and #drop_trigger should create and drop triggers" do
|
|
675
|
+
@d.create_language(:plpgsql)
|
|
676
|
+
@d.create_function(:tf, 'BEGIN IF NEW.value IS NULL THEN RAISE EXCEPTION \'Blah\'; END IF; RETURN NEW; END;', :language=>:plpgsql, :returns=>:trigger)
|
|
677
|
+
@d.create_trigger_sql(:test, :identity, :tf, :each_row=>true).should == 'CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON public.test FOR EACH ROW EXECUTE PROCEDURE tf()'
|
|
678
|
+
@d.create_trigger(:test, :identity, :tf, :each_row=>true)
|
|
679
|
+
@d[:test].insert(:name=>'a', :value=>1)
|
|
680
|
+
@d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>1}]
|
|
681
|
+
proc{@d[:test].filter(:name=>'a').update(:value=>nil)}.should raise_error(Sequel::DatabaseError)
|
|
682
|
+
@d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>1}]
|
|
683
|
+
@d[:test].filter(:name=>'a').update(:value=>3)
|
|
684
|
+
@d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>3}]
|
|
685
|
+
@d.drop_trigger_sql(:test, :identity).should == 'DROP TRIGGER identity ON public.test'
|
|
686
|
+
@d.drop_trigger(:test, :identity)
|
|
687
|
+
@d.create_trigger_sql(:test, :identity, :tf, :after=>true, :events=>:insert, :args=>[1, 'a']).should == 'CREATE TRIGGER identity AFTER INSERT ON public.test EXECUTE PROCEDURE tf(1, \'a\')'
|
|
688
|
+
@d.drop_trigger_sql(:test, :identity, :if_exists=>true, :cascade=>true).should == 'DROP TRIGGER IF EXISTS identity ON public.test CASCADE'
|
|
689
|
+
# Make sure if exists works
|
|
690
|
+
@d.drop_trigger(:test, :identity, :if_exists=>true, :cascade=>true)
|
|
691
|
+
end
|
|
692
|
+
end
|