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.
- data/CHANGELOG +1003 -0
- data/COPYING +18 -0
- data/README +81 -0
- data/Rakefile +176 -0
- data/bin/sequel +41 -0
- data/lib/sequel_core.rb +59 -0
- data/lib/sequel_core/adapters/adapter_skeleton.rb +68 -0
- data/lib/sequel_core/adapters/ado.rb +100 -0
- data/lib/sequel_core/adapters/db2.rb +158 -0
- data/lib/sequel_core/adapters/dbi.rb +126 -0
- data/lib/sequel_core/adapters/informix.rb +87 -0
- data/lib/sequel_core/adapters/jdbc.rb +108 -0
- data/lib/sequel_core/adapters/mysql.rb +269 -0
- data/lib/sequel_core/adapters/odbc.rb +145 -0
- data/lib/sequel_core/adapters/odbc_mssql.rb +93 -0
- data/lib/sequel_core/adapters/openbase.rb +90 -0
- data/lib/sequel_core/adapters/oracle.rb +99 -0
- data/lib/sequel_core/adapters/postgres.rb +519 -0
- data/lib/sequel_core/adapters/sqlite.rb +192 -0
- data/lib/sequel_core/array_keys.rb +296 -0
- data/lib/sequel_core/connection_pool.rb +152 -0
- data/lib/sequel_core/core_ext.rb +59 -0
- data/lib/sequel_core/core_sql.rb +191 -0
- data/lib/sequel_core/database.rb +433 -0
- data/lib/sequel_core/dataset.rb +409 -0
- data/lib/sequel_core/dataset/convenience.rb +321 -0
- data/lib/sequel_core/dataset/sequelizer.rb +354 -0
- data/lib/sequel_core/dataset/sql.rb +586 -0
- data/lib/sequel_core/exceptions.rb +45 -0
- data/lib/sequel_core/migration.rb +191 -0
- data/lib/sequel_core/model.rb +8 -0
- data/lib/sequel_core/pretty_table.rb +73 -0
- data/lib/sequel_core/schema.rb +8 -0
- data/lib/sequel_core/schema/schema_generator.rb +131 -0
- data/lib/sequel_core/schema/schema_sql.rb +131 -0
- data/lib/sequel_core/worker.rb +58 -0
- data/spec/adapters/informix_spec.rb +139 -0
- data/spec/adapters/mysql_spec.rb +330 -0
- data/spec/adapters/oracle_spec.rb +130 -0
- data/spec/adapters/postgres_spec.rb +189 -0
- data/spec/adapters/sqlite_spec.rb +345 -0
- data/spec/array_keys_spec.rb +679 -0
- data/spec/connection_pool_spec.rb +356 -0
- data/spec/core_ext_spec.rb +67 -0
- data/spec/core_sql_spec.rb +301 -0
- data/spec/database_spec.rb +812 -0
- data/spec/dataset_spec.rb +2381 -0
- data/spec/migration_spec.rb +261 -0
- data/spec/pretty_table_spec.rb +66 -0
- data/spec/rcov.opts +4 -0
- data/spec/schema_generator_spec.rb +86 -0
- data/spec/schema_spec.rb +230 -0
- data/spec/sequelizer_spec.rb +448 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/worker_spec.rb +96 -0
- 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
|