sequel 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/CHANGELOG +1551 -4
  2. data/README +306 -19
  3. data/Rakefile +84 -56
  4. data/bin/sequel +106 -0
  5. data/doc/cheat_sheet.rdoc +225 -0
  6. data/doc/dataset_filtering.rdoc +182 -0
  7. data/lib/sequel_core.rb +136 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +54 -0
  9. data/lib/sequel_core/adapters/ado.rb +80 -0
  10. data/lib/sequel_core/adapters/db2.rb +148 -0
  11. data/lib/sequel_core/adapters/dbi.rb +117 -0
  12. data/lib/sequel_core/adapters/informix.rb +78 -0
  13. data/lib/sequel_core/adapters/jdbc.rb +186 -0
  14. data/lib/sequel_core/adapters/jdbc/mysql.rb +55 -0
  15. data/lib/sequel_core/adapters/jdbc/postgresql.rb +66 -0
  16. data/lib/sequel_core/adapters/jdbc/sqlite.rb +47 -0
  17. data/lib/sequel_core/adapters/mysql.rb +231 -0
  18. data/lib/sequel_core/adapters/odbc.rb +155 -0
  19. data/lib/sequel_core/adapters/odbc_mssql.rb +106 -0
  20. data/lib/sequel_core/adapters/openbase.rb +64 -0
  21. data/lib/sequel_core/adapters/oracle.rb +170 -0
  22. data/lib/sequel_core/adapters/postgres.rb +199 -0
  23. data/lib/sequel_core/adapters/shared/mysql.rb +275 -0
  24. data/lib/sequel_core/adapters/shared/postgres.rb +351 -0
  25. data/lib/sequel_core/adapters/shared/sqlite.rb +146 -0
  26. data/lib/sequel_core/adapters/sqlite.rb +138 -0
  27. data/lib/sequel_core/connection_pool.rb +194 -0
  28. data/lib/sequel_core/core_ext.rb +203 -0
  29. data/lib/sequel_core/core_sql.rb +184 -0
  30. data/lib/sequel_core/database.rb +471 -0
  31. data/lib/sequel_core/database/schema.rb +156 -0
  32. data/lib/sequel_core/dataset.rb +457 -0
  33. data/lib/sequel_core/dataset/callback.rb +13 -0
  34. data/lib/sequel_core/dataset/convenience.rb +245 -0
  35. data/lib/sequel_core/dataset/pagination.rb +96 -0
  36. data/lib/sequel_core/dataset/query.rb +41 -0
  37. data/lib/sequel_core/dataset/schema.rb +15 -0
  38. data/lib/sequel_core/dataset/sql.rb +889 -0
  39. data/lib/sequel_core/deprecated.rb +26 -0
  40. data/lib/sequel_core/exceptions.rb +42 -0
  41. data/lib/sequel_core/migration.rb +187 -0
  42. data/lib/sequel_core/object_graph.rb +216 -0
  43. data/lib/sequel_core/pretty_table.rb +71 -0
  44. data/lib/sequel_core/schema.rb +2 -0
  45. data/lib/sequel_core/schema/generator.rb +239 -0
  46. data/lib/sequel_core/schema/sql.rb +325 -0
  47. data/lib/sequel_core/sql.rb +812 -0
  48. data/lib/sequel_model.rb +5 -1
  49. data/lib/sequel_model/association_reflection.rb +3 -8
  50. data/lib/sequel_model/base.rb +15 -10
  51. data/lib/sequel_model/inflector.rb +3 -5
  52. data/lib/sequel_model/plugins.rb +1 -1
  53. data/lib/sequel_model/record.rb +11 -3
  54. data/lib/sequel_model/schema.rb +4 -4
  55. data/lib/sequel_model/validations.rb +6 -1
  56. data/spec/adapters/ado_spec.rb +17 -0
  57. data/spec/adapters/informix_spec.rb +96 -0
  58. data/spec/adapters/mysql_spec.rb +764 -0
  59. data/spec/adapters/oracle_spec.rb +222 -0
  60. data/spec/adapters/postgres_spec.rb +441 -0
  61. data/spec/adapters/spec_helper.rb +7 -0
  62. data/spec/adapters/sqlite_spec.rb +400 -0
  63. data/spec/integration/dataset_test.rb +51 -0
  64. data/spec/integration/eager_loader_test.rb +702 -0
  65. data/spec/integration/schema_test.rb +102 -0
  66. data/spec/integration/spec_helper.rb +44 -0
  67. data/spec/integration/type_test.rb +43 -0
  68. data/spec/rcov.opts +2 -0
  69. data/spec/sequel_core/connection_pool_spec.rb +363 -0
  70. data/spec/sequel_core/core_ext_spec.rb +156 -0
  71. data/spec/sequel_core/core_sql_spec.rb +427 -0
  72. data/spec/sequel_core/database_spec.rb +964 -0
  73. data/spec/sequel_core/dataset_spec.rb +2977 -0
  74. data/spec/sequel_core/expression_filters_spec.rb +346 -0
  75. data/spec/sequel_core/migration_spec.rb +261 -0
  76. data/spec/sequel_core/object_graph_spec.rb +234 -0
  77. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  78. data/spec/sequel_core/schema_generator_spec.rb +122 -0
  79. data/spec/sequel_core/schema_spec.rb +497 -0
  80. data/spec/sequel_core/spec_helper.rb +51 -0
  81. data/spec/{association_reflection_spec.rb → sequel_model/association_reflection_spec.rb} +6 -6
  82. data/spec/{associations_spec.rb → sequel_model/associations_spec.rb} +47 -18
  83. data/spec/{base_spec.rb → sequel_model/base_spec.rb} +2 -1
  84. data/spec/{caching_spec.rb → sequel_model/caching_spec.rb} +0 -0
  85. data/spec/{dataset_methods_spec.rb → sequel_model/dataset_methods_spec.rb} +13 -1
  86. data/spec/{eager_loading_spec.rb → sequel_model/eager_loading_spec.rb} +75 -14
  87. data/spec/{hooks_spec.rb → sequel_model/hooks_spec.rb} +4 -4
  88. data/spec/sequel_model/inflector_spec.rb +119 -0
  89. data/spec/{model_spec.rb → sequel_model/model_spec.rb} +30 -11
  90. data/spec/{plugins_spec.rb → sequel_model/plugins_spec.rb} +0 -0
  91. data/spec/{record_spec.rb → sequel_model/record_spec.rb} +47 -6
  92. data/spec/{schema_spec.rb → sequel_model/schema_spec.rb} +18 -4
  93. data/spec/{spec_helper.rb → sequel_model/spec_helper.rb} +3 -2
  94. data/spec/{validations_spec.rb → sequel_model/validations_spec.rb} +37 -17
  95. data/spec/spec_config.rb +9 -0
  96. data/spec/spec_config.rb.example +10 -0
  97. metadata +110 -37
  98. data/spec/inflector_spec.rb +0 -34
@@ -0,0 +1,222 @@
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
+
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
+ end
42
+
43
+ context "An Oracle dataset" do
44
+ setup do
45
+ @d = ORACLE_DB[:items]
46
+ @d.delete # remove all records
47
+ end
48
+
49
+ specify "should return the correct record count" do
50
+ @d.count.should == 0
51
+ @d << {:name => 'abc', :value => 123}
52
+ @d << {:name => 'abc', :value => 456}
53
+ @d << {:name => 'def', :value => 789}
54
+ @d.count.should == 3
55
+ end
56
+
57
+ specify "should return the correct records" do
58
+ @d.to_a.should == []
59
+ @d << {:name => 'abc', :value => 123}
60
+ @d << {:name => 'abc', :value => 456}
61
+ @d << {:name => 'def', :value => 789}
62
+
63
+ @d.order(:value).to_a.should == [
64
+ {:name => 'abc', :value => 123},
65
+ {:name => 'abc', :value => 456},
66
+ {:name => 'def', :value => 789}
67
+ ]
68
+
69
+ @d.select(:name).uniq.order_by(:name).to_a.should == [
70
+ {:name => 'abc'},
71
+ {:name => 'def'}
72
+ ]
73
+
74
+ @d.order(:value.DESC).limit(1).to_a.should == [
75
+ {:name => 'def', :value => 789}
76
+ ]
77
+
78
+ @d.filter(:name => 'abc').to_a.should == [
79
+ {:name => 'abc', :value => 123},
80
+ {:name => 'abc', :value => 456}
81
+ ]
82
+
83
+ @d.order(:value.DESC).filter(:name => 'abc').to_a.should == [
84
+ {:name => 'abc', :value => 456},
85
+ {:name => 'abc', :value => 123}
86
+ ]
87
+
88
+ @d.filter(:name => 'abc').limit(1).to_a.should == [
89
+ {:name => 'abc', :value => 123}
90
+ ]
91
+
92
+ @d.filter(:name => 'abc').order(:value.DESC).limit(1).to_a.should == [
93
+ {:name => 'abc', :value => 456}
94
+ ]
95
+
96
+ @d.filter(:name => 'abc').order(:value).limit(1).to_a.should == [
97
+ {:name => 'abc', :value => 123}
98
+ ]
99
+
100
+ @d.order(:value).limit(1).to_a.should == [
101
+ {:name => 'abc', :value => 123}
102
+ ]
103
+
104
+ @d.order(:value).limit(1, 1).to_a.should == [
105
+ {:name => 'abc', :value => 456}
106
+ ]
107
+
108
+ @d.order(:value).limit(1, 2).to_a.should == [
109
+ {:name => 'def', :value => 789}
110
+ ]
111
+
112
+ @d.avg(:value).to_i.should == (789+123+456)/3
113
+
114
+ @d.max(:value).to_i.should == 789
115
+
116
+ @d.select(:name, :AVG[:value]).filter(:name => 'abc').group(:name).to_a.should == [
117
+ {:name => 'abc', :"avg(value)" => (456+123)/2.0}
118
+ ]
119
+
120
+ @d.select(:AVG[:value]).group(:name).order(:name).limit(1).to_a.should == [
121
+ {:"avg(value)" => (456+123)/2.0}
122
+ ]
123
+
124
+ @d.select(:name, :AVG[:value]).group(:name).order(:name).to_a.should == [
125
+ {:name => 'abc', :"avg(value)" => (456+123)/2.0},
126
+ {:name => 'def', :"avg(value)" => 789*1.0}
127
+ ]
128
+
129
+ @d.select(:name, :AVG[:value]).group(:name).order(:name).to_a.should == [
130
+ {:name => 'abc', :"avg(value)" => (456+123)/2.0},
131
+ {:name => 'def', :"avg(value)" => 789*1.0}
132
+ ]
133
+
134
+ @d.select(:name, :AVG[:value]).group(:name).having(:name => ['abc', 'def']).order(:name).to_a.should == [
135
+ {:name => 'abc', :"avg(value)" => (456+123)/2.0},
136
+ {:name => 'def', :"avg(value)" => 789*1.0}
137
+ ]
138
+
139
+ @d.select(:name, :value).filter(:name => 'abc').union(@d.select(:name, :value).filter(:name => 'def')).order(:value).to_a.should == [
140
+ {:name => 'abc', :value => 123},
141
+ {:name => 'abc', :value => 456},
142
+ {:name => 'def', :value => 789}
143
+ ]
144
+
145
+ end
146
+
147
+ specify "should update records correctly" do
148
+ @d << {:name => 'abc', :value => 123}
149
+ @d << {:name => 'abc', :value => 456}
150
+ @d << {:name => 'def', :value => 789}
151
+ @d.filter(:name => 'abc').update(:value => 530)
152
+
153
+ # the third record should stay the same
154
+ # floating-point precision bullshit
155
+ @d[:name => 'def'][:value].should == 789
156
+ @d.filter(:value => 530).count.should == 2
157
+ end
158
+
159
+ specify "should delete records correctly" do
160
+ @d << {:name => 'abc', :value => 123}
161
+ @d << {:name => 'abc', :value => 456}
162
+ @d << {:name => 'def', :value => 789}
163
+ @d.filter(:name => 'abc').delete
164
+
165
+ @d.count.should == 1
166
+ @d.first[:name].should == 'def'
167
+ end
168
+
169
+ specify "should be able to literalize booleans" do
170
+ proc {@d.literal(true)}.should_not raise_error
171
+ proc {@d.literal(false)}.should_not raise_error
172
+ end
173
+
174
+ specify "should support transactions" do
175
+ ORACLE_DB.transaction do
176
+ @d << {:name => 'abc', :value => 1}
177
+ end
178
+
179
+ @d.count.should == 1
180
+ end
181
+ end
182
+
183
+ context "Joined Oracle dataset" do
184
+ setup do
185
+ @d1 = ORACLE_DB[:books]
186
+ @d1.delete # remove all records
187
+ @d1 << {:id => 1, :title => 'aaa', :category_id => 100}
188
+ @d1 << {:id => 2, :title => 'bbb', :category_id => 100}
189
+ @d1 << {:id => 3, :title => 'ccc', :category_id => 101}
190
+ @d1 << {:id => 4, :title => 'ddd', :category_id => 102}
191
+
192
+ @d2 = ORACLE_DB[:categories]
193
+ @d2.delete # remove all records
194
+ @d2 << {:id => 100, :cat_name => 'ruby'}
195
+ @d2 << {:id => 101, :cat_name => 'rails'}
196
+ end
197
+
198
+ specify "should return correct result" do
199
+ @d1.join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id).to_a.should == [
200
+ {:id => 1, :title => 'aaa', :cat_name => 'ruby'},
201
+ {:id => 2, :title => 'bbb', :cat_name => 'ruby'},
202
+ {:id => 3, :title => 'ccc', :cat_name => 'rails'}
203
+ ]
204
+
205
+ @d1.join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id).limit(2, 1).to_a.should == [
206
+ {:id => 2, :title => 'bbb', :cat_name => 'ruby'},
207
+ {:id => 3, :title => 'ccc', :cat_name => 'rails'},
208
+ ]
209
+
210
+ @d1.left_outer_join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id).to_a.should == [
211
+ {:id => 1, :title => 'aaa', :cat_name => 'ruby'},
212
+ {:id => 2, :title => 'bbb', :cat_name => 'ruby'},
213
+ {:id => 3, :title => 'ccc', :cat_name => 'rails'},
214
+ {:id => 4, :title => 'ddd', :cat_name => nil}
215
+ ]
216
+
217
+ @d1.left_outer_join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id.DESC).limit(2, 0).to_a.should == [
218
+ {:id => 4, :title => 'ddd', :cat_name => nil},
219
+ {:id => 3, :title => 'ccc', :cat_name => 'rails'}
220
+ ]
221
+ end
222
+ end
@@ -0,0 +1,441 @@
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
+
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
+
25
+ context "A PostgreSQL database" do
26
+ setup do
27
+ @db = POSTGRES_DB
28
+ end
29
+
30
+ specify "should provide disconnect functionality" do
31
+ @db.tables
32
+ @db.pool.size.should == 1
33
+ @db.disconnect
34
+ @db.pool.size.should == 0
35
+ end
36
+
37
+ specify "should provide the server version" do
38
+ @db.server_version.should > 70000
39
+ end
40
+
41
+ specify "should raise Sequel::Error on error" do
42
+ proc{@db << "SELECT 1 + 'a'"}.should raise_error(Sequel::Error)
43
+ end
44
+
45
+ specify "should correctly parse the schema" do
46
+ @db.schema(:test3, :reload=>true).should == [
47
+ [:value, {:type=>:integer, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"integer", :numeric_precision=>32}],
48
+ [:time, {:type=>:datetime, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"timestamp without time zone", :numeric_precision=>nil}]
49
+ ]
50
+ @db.schema(:test4, :reload=>true).should == [
51
+ [:name, {:type=>:string, :allow_null=>true, :max_chars=>20, :default=>nil, :db_type=>"character varying", :numeric_precision=>nil}],
52
+ [:value, {:type=>:blob, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"bytea", :numeric_precision=>nil}]
53
+ ]
54
+ end
55
+
56
+ specify "should get the schema all database tables if no table name is used" do
57
+ @db.schema(:test3, :reload=>true).should == @db.schema(nil, :reload=>true)[:test3]
58
+ end
59
+ end
60
+
61
+ context "A PostgreSQL dataset" do
62
+ setup do
63
+ @d = POSTGRES_DB[:test]
64
+ @d.delete # remove all records
65
+ end
66
+
67
+ specify "should return the correct record count" do
68
+ @d.count.should == 0
69
+ @d << {:name => 'abc', :value => 123}
70
+ @d << {:name => 'abc', :value => 456}
71
+ @d << {:name => 'def', :value => 789}
72
+ @d.count.should == 3
73
+ end
74
+
75
+ specify "should return the correct records" do
76
+ @d.to_a.should == []
77
+ @d << {:name => 'abc', :value => 123}
78
+ @d << {:name => 'abc', :value => 456}
79
+ @d << {:name => 'def', :value => 789}
80
+
81
+ @d.order(:value).to_a.should == [
82
+ {:name => 'abc', :value => 123},
83
+ {:name => 'abc', :value => 456},
84
+ {:name => 'def', :value => 789}
85
+ ]
86
+ end
87
+
88
+ specify "should update records correctly" do
89
+ @d << {:name => 'abc', :value => 123}
90
+ @d << {:name => 'abc', :value => 456}
91
+ @d << {:name => 'def', :value => 789}
92
+ @d.filter(:name => 'abc').update(:value => 530)
93
+
94
+ # the third record should stay the same
95
+ # floating-point precision bullshit
96
+ @d[:name => 'def'][:value].should == 789
97
+ @d.filter(:value => 530).count.should == 2
98
+ end
99
+
100
+ specify "should delete records correctly" do
101
+ @d << {:name => 'abc', :value => 123}
102
+ @d << {:name => 'abc', :value => 456}
103
+ @d << {:name => 'def', :value => 789}
104
+ @d.filter(:name => 'abc').delete
105
+
106
+ @d.count.should == 1
107
+ @d.first[:name].should == 'def'
108
+ end
109
+
110
+ specify "should be able to literalize booleans" do
111
+ proc {@d.literal(true)}.should_not raise_error
112
+ proc {@d.literal(false)}.should_not raise_error
113
+ end
114
+
115
+ specify "should quote columns and tables using double quotes if quoting identifiers" do
116
+ @d.quote_identifiers = true
117
+ @d.select(:name).sql.should == \
118
+ 'SELECT "name" FROM "test"'
119
+
120
+ @d.select('COUNT(*)'.lit).sql.should == \
121
+ 'SELECT COUNT(*) FROM "test"'
122
+
123
+ @d.select(:max[:value]).sql.should == \
124
+ 'SELECT max("value") FROM "test"'
125
+
126
+ @d.select(:NOW[]).sql.should == \
127
+ 'SELECT NOW() FROM "test"'
128
+
129
+ @d.select(:max[:items__value]).sql.should == \
130
+ 'SELECT max("items"."value") FROM "test"'
131
+
132
+ @d.order(:name.desc).sql.should == \
133
+ 'SELECT * FROM "test" ORDER BY "name" DESC'
134
+
135
+ @d.select('test.name AS item_name'.lit).sql.should == \
136
+ 'SELECT test.name AS item_name FROM "test"'
137
+
138
+ @d.select('"name"'.lit).sql.should == \
139
+ 'SELECT "name" FROM "test"'
140
+
141
+ @d.select('max(test."name") AS "max_name"'.lit).sql.should == \
142
+ 'SELECT max(test."name") AS "max_name" FROM "test"'
143
+
144
+ @d.select(:test[:abc, 'hello']).sql.should == \
145
+ "SELECT test(\"abc\", 'hello') FROM \"test\""
146
+
147
+ @d.select(:test[:abc__def, 'hello']).sql.should == \
148
+ "SELECT test(\"abc\".\"def\", 'hello') FROM \"test\""
149
+
150
+ @d.select(:test[:abc__def, 'hello'].as(:x2)).sql.should == \
151
+ "SELECT test(\"abc\".\"def\", 'hello') AS \"x2\" FROM \"test\""
152
+
153
+ @d.insert_sql(:value => 333).should == \
154
+ 'INSERT INTO "test" ("value") VALUES (333)'
155
+
156
+ @d.insert_sql(:x => :y).should == \
157
+ 'INSERT INTO "test" ("x") VALUES ("y")'
158
+ end
159
+
160
+ specify "should quote fields correctly when reversing the order if quoting identifiers" do
161
+ @d.quote_identifiers = true
162
+ @d.reverse_order(:name).sql.should == \
163
+ 'SELECT * FROM "test" ORDER BY "name" DESC'
164
+
165
+ @d.reverse_order(:name.desc).sql.should == \
166
+ 'SELECT * FROM "test" ORDER BY "name" ASC'
167
+
168
+ @d.reverse_order(:name, :test.desc).sql.should == \
169
+ 'SELECT * FROM "test" ORDER BY "name" DESC, "test" ASC'
170
+
171
+ @d.reverse_order(:name.desc, :test).sql.should == \
172
+ 'SELECT * FROM "test" ORDER BY "name" ASC, "test" DESC'
173
+ end
174
+
175
+ specify "should support transactions" do
176
+ POSTGRES_DB.transaction do
177
+ @d << {:name => 'abc', :value => 1}
178
+ end
179
+
180
+ @d.count.should == 1
181
+ end
182
+
183
+ specify "should have #transaction yield the connection" do
184
+ POSTGRES_DB.transaction do |conn|
185
+ conn.should_not == nil
186
+ end
187
+ end
188
+
189
+ specify "should correctly rollback transactions" do
190
+ proc do
191
+ POSTGRES_DB.transaction do
192
+ @d << {:name => 'abc', :value => 1}
193
+ raise Interrupt, 'asdf'
194
+ end
195
+ end.should raise_error(Interrupt)
196
+
197
+ @d.count.should == 0
198
+ end
199
+
200
+ specify "should handle returning inside of the block by committing" do
201
+ def POSTGRES_DB.ret_commit
202
+ transaction do
203
+ self[:test] << {:name => 'abc'}
204
+ return
205
+ self[:test] << {:name => 'd'}
206
+ end
207
+ end
208
+ @d.count.should == 0
209
+ POSTGRES_DB.ret_commit
210
+ @d.count.should == 1
211
+ POSTGRES_DB.ret_commit
212
+ @d.count.should == 2
213
+ proc do
214
+ POSTGRES_DB.transaction do
215
+ raise Interrupt, 'asdf'
216
+ end
217
+ end.should raise_error(Interrupt)
218
+
219
+ @d.count.should == 2
220
+ end
221
+
222
+ specify "should support nested transactions through savepoints" do
223
+ POSTGRES_DB.transaction do
224
+ @d << {:name => '1'}
225
+ POSTGRES_DB.transaction do
226
+ @d << {:name => '2'}
227
+ POSTGRES_DB.transaction do
228
+ @d << {:name => '3'}
229
+ raise Sequel::Error::Rollback
230
+ end
231
+ @d << {:name => '4'}
232
+ POSTGRES_DB.transaction do
233
+ @d << {:name => '6'}
234
+ POSTGRES_DB.transaction do
235
+ @d << {:name => '7'}
236
+ end
237
+ raise Sequel::Error::Rollback
238
+ end
239
+ @d << {:name => '5'}
240
+ end
241
+ end
242
+
243
+ @d.count.should == 4
244
+ @d.order(:name).map(:name).should == %w{1 2 4 5}
245
+ end
246
+
247
+ specify "should support regexps" do
248
+ @d << {:name => 'abc', :value => 1}
249
+ @d << {:name => 'bcd', :value => 2}
250
+ @d.filter(:name => /bc/).count.should == 2
251
+ @d.filter(:name => /^bc/).count.should == 1
252
+ end
253
+
254
+ specify "should correctly escape strings" do
255
+ POSTGRES_DB['SELECT ? AS a', "\\dingo"].get(:a) == "\\dingo"
256
+ end
257
+
258
+ specify "should properly escape binary data" do
259
+ POSTGRES_DB['SELECT ? AS a', "\1\2\3".to_blob].get(:a) == "\1\2\3"
260
+ end
261
+
262
+ specify "should retrieve binary data as Blob object" do
263
+ d = POSTGRES_DB[:test4]
264
+ d << {:name => '123', :value => "\1\2\3".to_blob}
265
+ retrieved_binary_value = d[:name => '123'][:value]
266
+ retrieved_binary_value.should be_a_kind_of(::Sequel::SQL::Blob)
267
+ retrieved_binary_value.should == "\1\2\3"
268
+ retrieved_binary_value = d[:value => "\1\2\3".to_blob][:value]
269
+ retrieved_binary_value.should be_a_kind_of(::Sequel::SQL::Blob)
270
+ retrieved_binary_value.should == "\1\2\3"
271
+ end
272
+
273
+ specify "should properly receive binary data" do
274
+ POSTGRES_DB['SELECT ?::bytea AS a', "a"].get(:a) == "a"
275
+ end
276
+ end
277
+
278
+ context "A PostgreSQL dataaset with a timestamp field" do
279
+ setup do
280
+ @d = POSTGRES_DB[:test3]
281
+ @d.delete
282
+ end
283
+
284
+ specify "should store milliseconds in time fields" do
285
+ t = Time.now
286
+ @d << {:value=>1, :time=>t}
287
+ @d.literal(@d[:value =>'1'][:time]).should == @d.literal(t)
288
+ @d[:value=>'1'][:time].usec.should == t.usec
289
+ end
290
+ end
291
+
292
+ context "A PostgreSQL database" do
293
+ setup do
294
+ @db = POSTGRES_DB
295
+ end
296
+
297
+ specify "should support column operations" do
298
+ @db.create_table!(:test2){text :name; integer :value}
299
+ @db[:test2] << {}
300
+ @db[:test2].columns.should == [:name, :value]
301
+
302
+ @db.add_column :test2, :xyz, :text, :default => '000'
303
+ @db[:test2].columns.should == [:name, :value, :xyz]
304
+ @db[:test2] << {:name => 'mmm', :value => 111}
305
+ @db[:test2].first[:xyz].should == '000'
306
+
307
+ @db[:test2].columns.should == [:name, :value, :xyz]
308
+ @db.drop_column :test2, :xyz
309
+
310
+ @db[:test2].columns.should == [:name, :value]
311
+
312
+ @db[:test2].delete
313
+ @db.add_column :test2, :xyz, :text, :default => '000'
314
+ @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 'qqqq'}
315
+
316
+ @db[:test2].columns.should == [:name, :value, :xyz]
317
+ @db.rename_column :test2, :xyz, :zyx
318
+ @db[:test2].columns.should == [:name, :value, :zyx]
319
+ @db[:test2].first[:zyx].should == 'qqqq'
320
+
321
+ @db.add_column :test2, :xyz, :float
322
+ @db[:test2].delete
323
+ @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
324
+ @db.set_column_type :test2, :xyz, :integer
325
+
326
+ @db[:test2].first[:xyz].should == 57
327
+ end
328
+ end
329
+
330
+ context "A PostgreSQL database" do
331
+ setup do
332
+ end
333
+
334
+ specify "should support fulltext indexes" do
335
+ g = Sequel::Schema::Generator.new(POSTGRES_DB) do
336
+ text :title
337
+ text :body
338
+ full_text_index [:title, :body]
339
+ end
340
+ POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
341
+ "CREATE TABLE posts (title text, body text)",
342
+ "CREATE INDEX posts_title_body_index ON posts USING gin (to_tsvector(title || body))"
343
+ ]
344
+ end
345
+
346
+ specify "should support fulltext indexes with a specific language" do
347
+ g = Sequel::Schema::Generator.new(POSTGRES_DB) do
348
+ text :title
349
+ text :body
350
+ full_text_index [:title, :body], :language => 'french'
351
+ end
352
+ POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
353
+ "CREATE TABLE posts (title text, body text)",
354
+ "CREATE INDEX posts_title_body_index ON posts USING gin (to_tsvector('french', title || body))"
355
+ ]
356
+ end
357
+
358
+ specify "should support full_text_search" do
359
+ POSTGRES_DB[:posts].full_text_search(:title, 'ruby').sql.should ==
360
+ "SELECT * FROM posts WHERE (to_tsvector(title) @@ to_tsquery('ruby'))"
361
+
362
+ POSTGRES_DB[:posts].full_text_search([:title, :body], ['ruby', 'sequel']).sql.should ==
363
+ "SELECT * FROM posts WHERE (to_tsvector(title || body) @@ to_tsquery('ruby | sequel'))"
364
+
365
+ POSTGRES_DB[:posts].full_text_search(:title, 'ruby', :language => 'french').sql.should ==
366
+ "SELECT * FROM posts WHERE (to_tsvector('french', title) @@ to_tsquery('french', 'ruby'))"
367
+ end
368
+
369
+ specify "should support spatial indexes" do
370
+ g = Sequel::Schema::Generator.new(POSTGRES_DB) do
371
+ geometry :geom
372
+ spatial_index [:geom]
373
+ end
374
+ POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
375
+ "CREATE TABLE posts (geom geometry)",
376
+ "CREATE INDEX posts_geom_index ON posts USING gist (geom)"
377
+ ]
378
+ end
379
+
380
+ specify "should support indexes with index type" do
381
+ g = Sequel::Schema::Generator.new(POSTGRES_DB) do
382
+ varchar :title, :size => 5
383
+ index :title, :type => 'hash'
384
+ end
385
+ POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
386
+ "CREATE TABLE posts (title varchar(5))",
387
+ "CREATE INDEX posts_title_index ON posts USING hash (title)"
388
+ ]
389
+ end
390
+
391
+ specify "should support unique indexes with index type" do
392
+ g = Sequel::Schema::Generator.new(POSTGRES_DB) do
393
+ varchar :title, :size => 5
394
+ index :title, :type => 'hash', :unique => true
395
+ end
396
+ POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
397
+ "CREATE TABLE posts (title varchar(5))",
398
+ "CREATE UNIQUE INDEX posts_title_index ON posts USING hash (title)"
399
+ ]
400
+ end
401
+
402
+ specify "should support partial indexes" do
403
+ g = Sequel::Schema::Generator.new(POSTGRES_DB) do
404
+ varchar :title, :size => 5
405
+ index :title, :where => {:something => 5}
406
+ end
407
+ POSTGRES_DB.create_table_sql_list(:posts, *g.create_info).should == [
408
+ "CREATE TABLE posts (title varchar(5))",
409
+ "CREATE INDEX posts_title_index ON posts (title) WHERE (something = 5)"
410
+ ]
411
+ end
412
+ end
413
+
414
+ context "Postgres::Dataset#multi_insert_sql / #import" do
415
+ setup do
416
+ @ds = POSTGRES_DB[:test]
417
+ end
418
+
419
+ specify "should return separate insert statements if server_version < 80200" do
420
+ @ds.db.meta_def(:server_version) {80199}
421
+
422
+ @ds.multi_insert_sql([:x, :y], [[1, 2], [3, 4]]).should == [
423
+ 'INSERT INTO test (x, y) VALUES (1, 2)',
424
+ 'INSERT INTO test (x, y) VALUES (3, 4)'
425
+ ]
426
+ end
427
+
428
+ specify "should a single insert statement if server_version >= 80200" do
429
+ @ds.db.meta_def(:server_version) {80200}
430
+
431
+ @ds.multi_insert_sql([:x, :y], [[1, 2], [3, 4]]).should == [
432
+ 'INSERT INTO test (x, y) VALUES (1, 2), (3, 4)'
433
+ ]
434
+
435
+ @ds.db.meta_def(:server_version) {80201}
436
+
437
+ @ds.multi_insert_sql([:x, :y], [[1, 2], [3, 4]]).should == [
438
+ 'INSERT INTO test (x, y) VALUES (1, 2), (3, 4)'
439
+ ]
440
+ end
441
+ end