sequel_core 1.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.
Files changed (57) hide show
  1. data/CHANGELOG +1003 -0
  2. data/COPYING +18 -0
  3. data/README +81 -0
  4. data/Rakefile +176 -0
  5. data/bin/sequel +41 -0
  6. data/lib/sequel_core.rb +59 -0
  7. data/lib/sequel_core/adapters/adapter_skeleton.rb +68 -0
  8. data/lib/sequel_core/adapters/ado.rb +100 -0
  9. data/lib/sequel_core/adapters/db2.rb +158 -0
  10. data/lib/sequel_core/adapters/dbi.rb +126 -0
  11. data/lib/sequel_core/adapters/informix.rb +87 -0
  12. data/lib/sequel_core/adapters/jdbc.rb +108 -0
  13. data/lib/sequel_core/adapters/mysql.rb +269 -0
  14. data/lib/sequel_core/adapters/odbc.rb +145 -0
  15. data/lib/sequel_core/adapters/odbc_mssql.rb +93 -0
  16. data/lib/sequel_core/adapters/openbase.rb +90 -0
  17. data/lib/sequel_core/adapters/oracle.rb +99 -0
  18. data/lib/sequel_core/adapters/postgres.rb +519 -0
  19. data/lib/sequel_core/adapters/sqlite.rb +192 -0
  20. data/lib/sequel_core/array_keys.rb +296 -0
  21. data/lib/sequel_core/connection_pool.rb +152 -0
  22. data/lib/sequel_core/core_ext.rb +59 -0
  23. data/lib/sequel_core/core_sql.rb +191 -0
  24. data/lib/sequel_core/database.rb +433 -0
  25. data/lib/sequel_core/dataset.rb +409 -0
  26. data/lib/sequel_core/dataset/convenience.rb +321 -0
  27. data/lib/sequel_core/dataset/sequelizer.rb +354 -0
  28. data/lib/sequel_core/dataset/sql.rb +586 -0
  29. data/lib/sequel_core/exceptions.rb +45 -0
  30. data/lib/sequel_core/migration.rb +191 -0
  31. data/lib/sequel_core/model.rb +8 -0
  32. data/lib/sequel_core/pretty_table.rb +73 -0
  33. data/lib/sequel_core/schema.rb +8 -0
  34. data/lib/sequel_core/schema/schema_generator.rb +131 -0
  35. data/lib/sequel_core/schema/schema_sql.rb +131 -0
  36. data/lib/sequel_core/worker.rb +58 -0
  37. data/spec/adapters/informix_spec.rb +139 -0
  38. data/spec/adapters/mysql_spec.rb +330 -0
  39. data/spec/adapters/oracle_spec.rb +130 -0
  40. data/spec/adapters/postgres_spec.rb +189 -0
  41. data/spec/adapters/sqlite_spec.rb +345 -0
  42. data/spec/array_keys_spec.rb +679 -0
  43. data/spec/connection_pool_spec.rb +356 -0
  44. data/spec/core_ext_spec.rb +67 -0
  45. data/spec/core_sql_spec.rb +301 -0
  46. data/spec/database_spec.rb +812 -0
  47. data/spec/dataset_spec.rb +2381 -0
  48. data/spec/migration_spec.rb +261 -0
  49. data/spec/pretty_table_spec.rb +66 -0
  50. data/spec/rcov.opts +4 -0
  51. data/spec/schema_generator_spec.rb +86 -0
  52. data/spec/schema_spec.rb +230 -0
  53. data/spec/sequelizer_spec.rb +448 -0
  54. data/spec/spec.opts +5 -0
  55. data/spec/spec_helper.rb +44 -0
  56. data/spec/worker_spec.rb +96 -0
  57. metadata +162 -0
@@ -0,0 +1,131 @@
1
+ module Sequel
2
+ module Schema
3
+ module SQL
4
+ RESTRICT = 'RESTRICT'.freeze
5
+ CASCADE = 'CASCADE'.freeze
6
+ NO_ACTION = 'NO ACTION'.freeze
7
+ SET_NULL = 'SET NULL'.freeze
8
+ SET_DEFAULT = 'SET DEFAULT'.freeze
9
+
10
+ def on_delete_clause(action)
11
+ case action
12
+ when :restrict
13
+ RESTRICT
14
+ when :cascade
15
+ CASCADE
16
+ when :set_null
17
+ SET_NULL
18
+ when :set_default
19
+ SET_DEFAULT
20
+ else
21
+ NO_ACTION
22
+ end
23
+ end
24
+
25
+ AUTOINCREMENT = 'AUTOINCREMENT'.freeze
26
+
27
+ def auto_increment_sql
28
+ AUTOINCREMENT
29
+ end
30
+
31
+ COMMA_SEPARATOR = ', '.freeze
32
+ UNIQUE = ' UNIQUE'.freeze
33
+ NOT_NULL = ' NOT NULL'.freeze
34
+ PRIMARY_KEY = ' PRIMARY KEY'.freeze
35
+
36
+ TYPES = Hash.new {|h, k| k}
37
+ TYPES[:double] = 'double precision'
38
+
39
+ def schema_utility_dataset
40
+ @schema_utility_dataset ||= dataset
41
+ end
42
+
43
+ def literal(v)
44
+ schema_utility_dataset.literal(v)
45
+ end
46
+
47
+ def column_definition_sql(column)
48
+ sql = "#{literal(column[:name].to_sym)} #{TYPES[column[:type]]}"
49
+ column[:size] ||= 255 if column[:type] == :varchar
50
+ elements = column[:size] || column[:elements]
51
+ sql << "(#{literal(elements)})" if elements
52
+ sql << UNIQUE if column[:unique]
53
+ sql << NOT_NULL if column[:null] == false
54
+ sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
55
+ sql << PRIMARY_KEY if column[:primary_key]
56
+ if column[:table]
57
+ sql << " REFERENCES #{column[:table]}"
58
+ sql << "(#{column[:key]})" if column[:key]
59
+ end
60
+ sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
61
+ sql << " #{auto_increment_sql}" if column[:auto_increment]
62
+ sql
63
+ end
64
+
65
+ def column_list_sql(columns)
66
+ columns.map {|c| column_definition_sql(c)}.join(COMMA_SEPARATOR)
67
+ end
68
+
69
+ UNDERSCORE = '_'.freeze
70
+
71
+ def default_index_name(table_name, columns)
72
+ "#{table_name}_#{columns.join(UNDERSCORE)}_index"
73
+ end
74
+
75
+ def index_definition_sql(table_name, index)
76
+ index_name = index[:name] || default_index_name(table_name, index[:columns])
77
+ if index[:unique]
78
+ "CREATE UNIQUE INDEX #{index_name} ON #{table_name} (#{literal(index[:columns])})"
79
+ else
80
+ "CREATE INDEX #{index_name} ON #{table_name} (#{literal(index[:columns])})"
81
+ end
82
+ end
83
+
84
+ def index_list_sql_list(table_name, indexes)
85
+ indexes.map {|i| index_definition_sql(table_name, i)}
86
+ end
87
+
88
+ def create_table_sql_list(name, columns, indexes = nil)
89
+ sql = ["CREATE TABLE #{name} (#{column_list_sql(columns)})"]
90
+ if indexes && !indexes.empty?
91
+ sql.concat(index_list_sql_list(name, indexes))
92
+ end
93
+ sql
94
+ end
95
+
96
+ def drop_table_sql(name)
97
+ "DROP TABLE #{name}"
98
+ end
99
+
100
+ def rename_table_sql(name, new_name)
101
+ "ALTER TABLE #{name} RENAME TO #{new_name}"
102
+ end
103
+
104
+ def alter_table_sql_list(table, operations)
105
+ operations.map {|op| alter_table_sql(table, op)}
106
+ end
107
+
108
+ def alter_table_sql(table, op)
109
+ case op[:op]
110
+ when :add_column
111
+ "ALTER TABLE #{table} ADD COLUMN #{column_definition_sql(op)}"
112
+ when :drop_column
113
+ "ALTER TABLE #{table} DROP COLUMN #{literal(op[:name])}"
114
+ when :rename_column
115
+ "ALTER TABLE #{table} RENAME COLUMN #{literal(op[:name])} TO #{literal(op[:new_name])}"
116
+ when :set_column_type
117
+ "ALTER TABLE #{table} ALTER COLUMN #{literal(op[:name])} TYPE #{op[:type]}"
118
+ when :set_column_default
119
+ "ALTER TABLE #{table} ALTER COLUMN #{literal(op[:name])} SET DEFAULT #{literal(op[:default])}"
120
+ when :add_index
121
+ index_definition_sql(table, op)
122
+ when :drop_index
123
+ "DROP INDEX #{default_index_name(table, op[:columns])}"
124
+ else
125
+ raise Error, "Unsupported ALTER TABLE operation"
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+
@@ -0,0 +1,58 @@
1
+ require "thread"
2
+
3
+ module Sequel
4
+
5
+ class Worker < Thread
6
+
7
+ attr_reader :queue
8
+ attr_reader :errors
9
+
10
+ def initialize(db = nil)
11
+ @queue = Queue.new
12
+ @errors = []
13
+ t = self
14
+ t.abort_on_exception = true
15
+ @transaction = !db.nil?
16
+ db ? super {db.transaction {t.work}} : super {t.work}
17
+ end
18
+
19
+ def work
20
+ loop {next_job}
21
+ rescue Sequel::Error::WorkerStop # signals the worker thread to stop
22
+ ensure
23
+ rollback! if @transaction && !@errors.empty?
24
+ end
25
+
26
+ def busy?
27
+ @cur || !@queue.empty?
28
+ end
29
+
30
+ def async(proc = nil, &block)
31
+ @queue << (proc || block)
32
+ self
33
+ end
34
+ alias_method :add, :async
35
+ alias_method :<<, :async
36
+
37
+ def join
38
+ while busy?
39
+ sleep 0.1
40
+ end
41
+ self.raise Error::WorkerStop
42
+ super
43
+ end
44
+
45
+ private
46
+ def next_job
47
+ @cur = @queue.pop
48
+ @cur.call
49
+ rescue Error::WorkerStop => e
50
+ raise e
51
+ rescue Exception => e
52
+ @errors << e
53
+ ensure
54
+ @cur = nil
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,139 @@
1
+ require File.join(File.dirname(__FILE__), '../../lib/sequel')
2
+
3
+ INFORMIX_DB = Sequel('informix://localhost/mydb')
4
+ if INFORMIX_DB.table_exists?(:test)
5
+ INFORMIX_DB.drop_table :test
6
+ end
7
+ INFORMIX_DB.create_table :test do
8
+ text :name
9
+ integer :value
10
+
11
+ index :value
12
+ end
13
+
14
+ context "A Informix database" do
15
+ specify "should provide disconnect functionality" do
16
+ INFORMIX_DB.execute("select user from dual")
17
+ INFORMIX_DB.pool.size.should == 1
18
+ INFORMIX_DB.disconnect
19
+ INFORMIX_DB.pool.size.should == 0
20
+ end
21
+ end
22
+
23
+ context "A Informix dataset" do
24
+ setup do
25
+ @d = INFORMIX_DB[:test]
26
+ @d.delete # remove all records
27
+ end
28
+
29
+ specify "should return the correct record count" do
30
+ @d.count.should == 0
31
+ @d << {:name => 'abc', :value => 123}
32
+ @d << {:name => 'abc', :value => 456}
33
+ @d << {:name => 'def', :value => 789}
34
+ @d.count.should == 3
35
+ end
36
+
37
+ specify "should return the correct records" do
38
+ @d.to_a.should == []
39
+ @d << {:name => 'abc', :value => 123}
40
+ @d << {:name => 'abc', :value => 456}
41
+ @d << {:name => 'def', :value => 789}
42
+
43
+ @d.order(:value).to_a.should == [
44
+ {:name => 'abc', :value => 123},
45
+ {:name => 'abc', :value => 456},
46
+ {:name => 'def', :value => 789}
47
+ ]
48
+ end
49
+
50
+ specify "should update records correctly" do
51
+ @d << {:name => 'abc', :value => 123}
52
+ @d << {:name => 'abc', :value => 456}
53
+ @d << {:name => 'def', :value => 789}
54
+ @d.filter(:name => 'abc').update(:value => 530)
55
+
56
+ # the third record should stay the same
57
+ # floating-point precision bullshit
58
+ @d[:name => 'def'][:value].should == 789
59
+ @d.filter(:value => 530).count.should == 2
60
+ end
61
+
62
+ specify "should delete records correctly" do
63
+ @d << {:name => 'abc', :value => 123}
64
+ @d << {:name => 'abc', :value => 456}
65
+ @d << {:name => 'def', :value => 789}
66
+ @d.filter(:name => 'abc').delete
67
+
68
+ @d.count.should == 1
69
+ @d.first[:name].should == 'def'
70
+ end
71
+
72
+ specify "should be able to literalize booleans" do
73
+ proc {@d.literal(true)}.should_not raise_error
74
+ proc {@d.literal(false)}.should_not raise_error
75
+ end
76
+
77
+ specify "should support transactions" do
78
+ INFORMIX_DB.transaction do
79
+ @d << {:name => 'abc', :value => 1}
80
+ end
81
+
82
+ @d.count.should == 1
83
+ end
84
+
85
+ specify "should support #first and #last" do
86
+ @d << {:name => 'abc', :value => 123}
87
+ @d << {:name => 'abc', :value => 456}
88
+ @d << {:name => 'def', :value => 789}
89
+
90
+ @d.order(:value).first.should == {:name => 'abc', :value => 123}
91
+ @d.order(:value).last.should == {:name => 'def', :value => 789}
92
+ end
93
+ end
94
+
95
+ context "A Informix dataset in array tuples mode" do
96
+ setup do
97
+ @d = INFORMIX_DB[:test]
98
+ @d.delete # remove all records
99
+ Sequel.use_array_tuples
100
+ end
101
+
102
+ teardown do
103
+ Sequel.use_hash_tuples
104
+ end
105
+
106
+ specify "should return the correct records" do
107
+ @d.to_a.should == []
108
+ @d << {:name => 'abc', :value => 123}
109
+ @d << {:name => 'abc', :value => 456}
110
+ @d << {:name => 'def', :value => 789}
111
+
112
+ @d.order(:value).select(:name, :value).to_a.should == [
113
+ ['abc', 123],
114
+ ['abc', 456],
115
+ ['def', 789]
116
+ ]
117
+ end
118
+
119
+ specify "should work correctly with transforms" do
120
+ @d.transform(:value => [proc {|v| v.to_s}, proc {|v| v.to_i}])
121
+
122
+ @d.to_a.should == []
123
+ @d << {:name => 'abc', :value => 123}
124
+ @d << {:name => 'abc', :value => 456}
125
+ @d << {:name => 'def', :value => 789}
126
+
127
+ @d.order(:value).select(:name, :value).to_a.should == [
128
+ ['abc', '123'],
129
+ ['abc', '456'],
130
+ ['def', '789']
131
+ ]
132
+
133
+ a = @d.order(:value).first
134
+ a.values.should == ['abc', '123']
135
+ a.keys.should == [:name, :value]
136
+ a[:name].should == 'abc'
137
+ a[:value].should == '123'
138
+ end
139
+ end
@@ -0,0 +1,330 @@
1
+ require File.join(File.dirname(__FILE__), '../../lib/sequel')
2
+ require 'logger'
3
+
4
+ MYSQL_DB = Sequel('mysql://root@localhost/sandbox')
5
+ MYSQL_DB.drop_table(:items) if MYSQL_DB.table_exists?(:items)
6
+ MYSQL_DB.drop_table(:test2) if MYSQL_DB.table_exists?(:test2)
7
+ MYSQL_DB.create_table :items do
8
+ text :name
9
+ integer :value, :index => true
10
+ end
11
+ MYSQL_DB.create_table :test2 do
12
+ text :name
13
+ integer :value
14
+ end
15
+
16
+ context "A MySQL database" do
17
+ setup do
18
+ @db = MYSQL_DB
19
+ end
20
+
21
+ specify "should provide disconnect functionality" do
22
+ @db.tables
23
+ @db.pool.size.should == 1
24
+ @db.disconnect
25
+ @db.pool.size.should == 0
26
+ end
27
+ end
28
+
29
+ context "A MySQL dataset" do
30
+ setup do
31
+ @d = MYSQL_DB[:items]
32
+ @d.delete # remove all records
33
+ end
34
+
35
+ specify "should return the correct record count" do
36
+ @d.count.should == 0
37
+ @d << {:name => 'abc', :value => 123}
38
+ @d << {:name => 'abc', :value => 456}
39
+ @d << {:name => 'def', :value => 789}
40
+ @d.count.should == 3
41
+ end
42
+
43
+ specify "should return the correct records" do
44
+ @d.to_a.should == []
45
+ @d << {:name => 'abc', :value => 123}
46
+ @d << {:name => 'abc', :value => 456}
47
+ @d << {:name => 'def', :value => 789}
48
+
49
+ @d.order(:value).to_a.should == [
50
+ {:name => 'abc', :value => 123},
51
+ {:name => 'abc', :value => 456},
52
+ {:name => 'def', :value => 789}
53
+ ]
54
+ end
55
+
56
+ specify "should update records correctly" do
57
+ @d << {:name => 'abc', :value => 123}
58
+ @d << {:name => 'abc', :value => 456}
59
+ @d << {:name => 'def', :value => 789}
60
+ @d.filter(:name => 'abc').update(:value => 530)
61
+
62
+ # the third record should stay the same
63
+ # floating-point precision bullshit
64
+ @d[:name => 'def'][:value].should == 789
65
+ @d.filter(:value => 530).count.should == 2
66
+ end
67
+
68
+ specify "should delete records correctly" do
69
+ @d << {:name => 'abc', :value => 123}
70
+ @d << {:name => 'abc', :value => 456}
71
+ @d << {:name => 'def', :value => 789}
72
+ @d.filter(:name => 'abc').delete
73
+
74
+ @d.count.should == 1
75
+ @d.first[:name].should == 'def'
76
+ end
77
+
78
+ specify "should be able to literalize booleans" do
79
+ proc {@d.literal(true)}.should_not raise_error
80
+ proc {@d.literal(false)}.should_not raise_error
81
+ end
82
+
83
+ specify "should quote columns using back-ticks" do
84
+ @d.select(:name).sql.should == \
85
+ 'SELECT `name` FROM items'
86
+
87
+ @d.select('COUNT(*)'.lit).sql.should == \
88
+ 'SELECT COUNT(*) FROM items'
89
+
90
+ @d.select(:value.MAX).sql.should == \
91
+ 'SELECT max(`value`) FROM items'
92
+
93
+ @d.select(:NOW[]).sql.should == \
94
+ 'SELECT NOW() FROM items'
95
+
96
+ @d.select(:items__value.MAX).sql.should == \
97
+ 'SELECT max(items.`value`) FROM items'
98
+
99
+ @d.order(:name.DESC).sql.should == \
100
+ 'SELECT * FROM items ORDER BY `name` DESC'
101
+
102
+ @d.select('items.name AS item_name'.lit).sql.should == \
103
+ 'SELECT items.name AS item_name FROM items'
104
+
105
+ @d.select('`name`'.lit).sql.should == \
106
+ 'SELECT `name` FROM items'
107
+
108
+ @d.select('max(items.`name`) AS `max_name`'.lit).sql.should == \
109
+ 'SELECT max(items.`name`) AS `max_name` FROM items'
110
+
111
+ @d.select(:test[:abc, 'hello']).sql.should == \
112
+ "SELECT test(`abc`, 'hello') FROM items"
113
+
114
+ @d.select(:test[:abc__def, 'hello']).sql.should == \
115
+ "SELECT test(abc.`def`, 'hello') FROM items"
116
+
117
+ @d.select(:test[:abc__def, 'hello'].as(:x2)).sql.should == \
118
+ "SELECT test(abc.`def`, 'hello') AS `x2` FROM items"
119
+
120
+ @d.insert_sql(:value => 333).should == \
121
+ 'INSERT INTO items (`value`) VALUES (333)'
122
+
123
+ @d.insert_sql(:x => :y).should == \
124
+ 'INSERT INTO items (`x`) VALUES (`y`)'
125
+ end
126
+
127
+ specify "should quote fields correctly when reversing the order" do
128
+ @d.reverse_order(:name).sql.should == \
129
+ 'SELECT * FROM items ORDER BY `name` DESC'
130
+
131
+ @d.reverse_order(:name.DESC).sql.should == \
132
+ 'SELECT * FROM items ORDER BY `name`'
133
+
134
+ @d.reverse_order(:name, :test.DESC).sql.should == \
135
+ 'SELECT * FROM items ORDER BY `name` DESC, `test`'
136
+
137
+ @d.reverse_order(:name.DESC, :test).sql.should == \
138
+ 'SELECT * FROM items ORDER BY `name`, `test` DESC'
139
+ end
140
+
141
+ specify "should support ORDER clause in UPDATE statements" do
142
+ @d.order(:name).update_sql(:value => 1).should == \
143
+ 'UPDATE items SET `value` = 1 ORDER BY `name`'
144
+ end
145
+
146
+ specify "should support LIMIT clause in UPDATE statements" do
147
+ @d.limit(10).update_sql(:value => 1).should == \
148
+ 'UPDATE items SET `value` = 1 LIMIT 10'
149
+ end
150
+
151
+ specify "should support transactions" do
152
+ MYSQL_DB.transaction do
153
+ @d << {:name => 'abc', :value => 1}
154
+ end
155
+
156
+ @d.count.should == 1
157
+ end
158
+
159
+ specify "should support regexps" do
160
+ @d << {:name => 'abc', :value => 1}
161
+ @d << {:name => 'bcd', :value => 2}
162
+ @d.filter(:name => /bc/).count.should == 2
163
+ @d.filter(:name => /^bc/).count.should == 1
164
+ end
165
+
166
+ specify "should correctly literalize strings with comment backslashes in them" do
167
+ @d.delete
168
+ proc {@d << {:name => ':\\'}}.should_not raise_error
169
+
170
+ @d.first[:name].should == ':\\'
171
+ end
172
+ end
173
+
174
+ context "A MySQL dataset in array tuples mode" do
175
+ setup do
176
+ @d = MYSQL_DB[:items]
177
+ @d.delete # remove all records
178
+ Sequel.use_array_tuples
179
+ end
180
+
181
+ teardown do
182
+ Sequel.use_hash_tuples
183
+ end
184
+
185
+ specify "should return the correct records" do
186
+ @d.to_a.should == []
187
+ @d << {:name => 'abc', :value => 123}
188
+ @d << {:name => 'abc', :value => 456}
189
+ @d << {:name => 'def', :value => 789}
190
+
191
+ @d.order(:value).select(:name, :value).to_a.should == [
192
+ ['abc', 123],
193
+ ['abc', 456],
194
+ ['def', 789]
195
+ ]
196
+ end
197
+
198
+ specify "should work correctly with transforms" do
199
+ @d.transform(:value => [proc {|v| v.to_s}, proc {|v| v.to_i}])
200
+
201
+ @d.to_a.should == []
202
+ @d << {:name => 'abc', :value => 123}
203
+ @d << {:name => 'abc', :value => 456}
204
+ @d << {:name => 'def', :value => 789}
205
+
206
+ @d.order(:value).select(:name, :value).to_a.should == [
207
+ ['abc', '123'],
208
+ ['abc', '456'],
209
+ ['def', '789']
210
+ ]
211
+
212
+ a = @d.order(:value).first
213
+ a.values.should == ['abc', '123']
214
+ a.keys.should == [:name, :value]
215
+ a[:name].should == 'abc'
216
+ a[:value].should == '123'
217
+ end
218
+ end
219
+
220
+ context "MySQL datasets" do
221
+ setup do
222
+ @d = MYSQL_DB[:orders]
223
+ end
224
+
225
+ specify "should correctly quote column references" do
226
+ market = 'ICE'
227
+ ack_stamp = Time.now - 15 * 60 # 15 minutes ago
228
+ @d.query do
229
+ select :market, :minute[:from_unixtime[:ack]].as(:minute)
230
+ where do
231
+ :ack > ack_stamp
232
+ :market == market
233
+ end
234
+ group_by :minute[:from_unixtime[:ack]]
235
+ end.sql.should == \
236
+ "SELECT `market`, minute(from_unixtime(`ack`)) AS `minute` FROM orders WHERE ((`ack` > #{@d.literal(ack_stamp)}) AND (`market` = 'ICE')) GROUP BY minute(from_unixtime(`ack`))"
237
+ end
238
+ end
239
+
240
+ # # Commented out because it was causing subsequent examples to fail for some reason
241
+ # context "Simple stored procedure test" do
242
+ # setup do
243
+ # # Create a simple stored procedure but drop it first if there
244
+ # MYSQL_DB.execute("DROP PROCEDURE IF EXISTS sp_get_server_id;")
245
+ # MYSQL_DB.execute("CREATE PROCEDURE sp_get_server_id() SQL SECURITY DEFINER SELECT @@SERVER_ID as server_id;")
246
+ # end
247
+ #
248
+ # specify "should return the server-id via a stored procedure call" do
249
+ # @server_id = MYSQL_DB["SELECT @@SERVER_ID as server_id;"].first[:server_id] # grab the server_id via a simple query
250
+ # @server_id_by_sp = MYSQL_DB["CALL sp_get_server_id();"].first[:server_id]
251
+ # @server_id_by_sp.should == @server_id # compare it to output from stored procedure
252
+ # end
253
+ # end
254
+ #
255
+ context "Joiמed MySQL dataset" do
256
+ setup do
257
+ @ds = MYSQL_DB[:nodes].join(:attributes, :node_id => :id)
258
+ end
259
+
260
+ specify "should quote fields correctly" do
261
+ @ds.sql.should == \
262
+ "SELECT * FROM nodes INNER JOIN attributes ON (attributes.`node_id` = nodes.`id`)"
263
+ end
264
+ end
265
+
266
+ context "A MySQL database" do
267
+ setup do
268
+ @db = MYSQL_DB
269
+ end
270
+
271
+ specify "should support add_column operations" do
272
+ @db.add_column :test2, :xyz, :text
273
+
274
+ @db[:test2].columns.should == [:name, :value, :xyz]
275
+ @db[:test2] << {:name => 'mmm', :value => 111, :xyz => '000'}
276
+ @db[:test2].first[:xyz].should == '000'
277
+ end
278
+
279
+ specify "should support drop_column operations" do
280
+ @db[:test2].columns.should == [:name, :value, :xyz]
281
+ @db.drop_column :test2, :xyz
282
+
283
+ @db[:test2].columns.should == [:name, :value]
284
+ end
285
+
286
+ specify "should support rename_column operations" do
287
+ @db[:test2].delete
288
+ @db.add_column :test2, :xyz, :text
289
+ @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 'qqqq'}
290
+
291
+ @db[:test2].columns.should == [:name, :value, :xyz]
292
+ @db.rename_column :test2, :xyz, :zyx, :type => :text
293
+ @db[:test2].columns.should == [:name, :value, :zyx]
294
+ @db[:test2].first[:zyx].should == 'qqqq'
295
+ end
296
+
297
+ specify "should support set_column_type operations" do
298
+ @db.add_column :test2, :xyz, :float
299
+ @db[:test2].delete
300
+ @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
301
+ @db.set_column_type :test2, :xyz, :integer
302
+
303
+ @db[:test2].first[:xyz].should == 57
304
+ end
305
+
306
+ specify "should support add_index" do
307
+ @db.add_index :test2, :value
308
+ end
309
+
310
+ specify "should support drop_index" do
311
+ @db.drop_index :test2, :value
312
+ end
313
+ end
314
+
315
+ context "A MySQL database" do
316
+ setup do
317
+ @db = MYSQL_DB
318
+ end
319
+
320
+ specify "should support defaults for boolean columns" do
321
+ g = Sequel::Schema::Generator.new(@db) do
322
+ boolean :active1, :default => true
323
+ boolean :active2, :default => false
324
+ end
325
+ statements = @db.create_table_sql_list(:items, *g.create_info)
326
+ statements.should == [
327
+ "CREATE TABLE items (`active1` boolean DEFAULT 1, `active2` boolean DEFAULT 0)"
328
+ ]
329
+ end
330
+ end