sequel_core 1.5.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +116 -0
- data/COPYING +19 -19
- data/README +83 -32
- data/Rakefile +9 -20
- data/bin/sequel +43 -112
- data/doc/cheat_sheet.rdoc +225 -0
- data/doc/dataset_filtering.rdoc +257 -0
- data/lib/sequel_core/adapters/adapter_skeleton.rb +4 -2
- data/lib/sequel_core/adapters/ado.rb +3 -1
- data/lib/sequel_core/adapters/db2.rb +4 -2
- data/lib/sequel_core/adapters/dbi.rb +127 -113
- data/lib/sequel_core/adapters/informix.rb +4 -2
- data/lib/sequel_core/adapters/jdbc.rb +5 -3
- data/lib/sequel_core/adapters/mysql.rb +112 -46
- data/lib/sequel_core/adapters/odbc.rb +5 -7
- data/lib/sequel_core/adapters/odbc_mssql.rb +12 -3
- data/lib/sequel_core/adapters/openbase.rb +3 -1
- data/lib/sequel_core/adapters/oracle.rb +11 -9
- data/lib/sequel_core/adapters/postgres.rb +261 -262
- data/lib/sequel_core/adapters/sqlite.rb +72 -22
- data/lib/sequel_core/connection_pool.rb +140 -73
- data/lib/sequel_core/core_ext.rb +201 -66
- data/lib/sequel_core/core_sql.rb +123 -153
- data/lib/sequel_core/database/schema.rb +156 -0
- data/lib/sequel_core/database.rb +321 -338
- data/lib/sequel_core/dataset/callback.rb +11 -12
- data/lib/sequel_core/dataset/convenience.rb +213 -240
- data/lib/sequel_core/dataset/pagination.rb +58 -43
- data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +331 -0
- data/lib/sequel_core/dataset/query.rb +41 -0
- data/lib/sequel_core/dataset/schema.rb +15 -0
- data/lib/sequel_core/dataset/sequelizer.rb +41 -373
- data/lib/sequel_core/dataset/sql.rb +741 -632
- data/lib/sequel_core/dataset.rb +183 -168
- data/lib/sequel_core/deprecated.rb +1 -169
- data/lib/sequel_core/exceptions.rb +24 -19
- data/lib/sequel_core/migration.rb +44 -52
- data/lib/sequel_core/object_graph.rb +43 -42
- data/lib/sequel_core/pretty_table.rb +71 -76
- data/lib/sequel_core/schema/generator.rb +163 -105
- data/lib/sequel_core/schema/sql.rb +250 -93
- data/lib/sequel_core/schema.rb +2 -8
- data/lib/sequel_core/sql.rb +394 -0
- data/lib/sequel_core/worker.rb +37 -27
- data/lib/sequel_core.rb +99 -45
- data/spec/adapters/informix_spec.rb +0 -1
- data/spec/adapters/mysql_spec.rb +177 -124
- data/spec/adapters/oracle_spec.rb +0 -1
- data/spec/adapters/postgres_spec.rb +98 -58
- data/spec/adapters/sqlite_spec.rb +45 -4
- data/spec/blockless_filters_spec.rb +269 -0
- data/spec/connection_pool_spec.rb +21 -18
- data/spec/core_ext_spec.rb +169 -19
- data/spec/core_sql_spec.rb +56 -49
- data/spec/database_spec.rb +78 -17
- data/spec/dataset_spec.rb +300 -428
- data/spec/migration_spec.rb +1 -1
- data/spec/object_graph_spec.rb +5 -11
- data/spec/rcov.opts +1 -1
- data/spec/schema_generator_spec.rb +16 -4
- data/spec/schema_spec.rb +89 -10
- data/spec/sequelizer_spec.rb +56 -56
- data/spec/spec.opts +0 -5
- data/spec/spec_config.rb +7 -0
- data/spec/spec_config.rb.example +5 -5
- data/spec/spec_helper.rb +6 -0
- data/spec/worker_spec.rb +1 -1
- metadata +78 -63
@@ -0,0 +1,225 @@
|
|
1
|
+
= Cheat Sheet
|
2
|
+
|
3
|
+
== Open a database
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'sequel'
|
7
|
+
|
8
|
+
DB = Sequel.sqlite 'my_blog.db'
|
9
|
+
DB = Sequel('postgres://user:password@localhost/my_db')
|
10
|
+
DB = Sequel.mysql 'my_db', :user => 'user', :password => 'password', :host => 'localhost'
|
11
|
+
DB = Sequel.ado 'mydb'
|
12
|
+
|
13
|
+
== Open an SQLite memory database
|
14
|
+
|
15
|
+
Without a filename argument, the sqlite adapter will setup a new sqlite database in RAM.
|
16
|
+
|
17
|
+
DB = Sequel.sqlite
|
18
|
+
|
19
|
+
== Logging SQL statements
|
20
|
+
|
21
|
+
require 'logger'
|
22
|
+
DB = Sequel.sqlite '', :loggers => [Logger.new($stdout)]
|
23
|
+
# or
|
24
|
+
DB.loggers << Logger.new(...)
|
25
|
+
|
26
|
+
== Using raw SQL
|
27
|
+
|
28
|
+
DB << "CREATE TABLE users (name VARCHAR(255) NOT NULL, age INT(3) NOT NULL)"
|
29
|
+
DB.fetch("SELECT name FROM users") do |row|
|
30
|
+
p r[:name]
|
31
|
+
end
|
32
|
+
dataset = DB["SELECT age FROM users"]
|
33
|
+
dataset.print
|
34
|
+
dataset.map(:age)
|
35
|
+
|
36
|
+
== Create a dataset
|
37
|
+
|
38
|
+
dataset = DB[:items]
|
39
|
+
dataset = DB.dataset.from(:items)
|
40
|
+
|
41
|
+
== Most dataset methods are chainable
|
42
|
+
|
43
|
+
dataset = DB[:managers].where(:salary => 5000..10000).order(:name, :department)
|
44
|
+
# or
|
45
|
+
dataset = DB.query do
|
46
|
+
from :managers
|
47
|
+
where :salary => 5000..10000
|
48
|
+
order :name, :department
|
49
|
+
end
|
50
|
+
|
51
|
+
== Insert rows
|
52
|
+
|
53
|
+
dataset.insert(:name => 'Sharon', :grade => 50)
|
54
|
+
dataset << {:name => 'Sharon', :grade => 50} # same effect as above
|
55
|
+
|
56
|
+
== Retrieve rows
|
57
|
+
|
58
|
+
dataset.each {|r| p r}
|
59
|
+
dataset.all #=> [{...}, {...}, ...]
|
60
|
+
dataset.first
|
61
|
+
dataset.order(:name).last # works only for ordered datasets
|
62
|
+
|
63
|
+
== Update/Delete rows
|
64
|
+
|
65
|
+
dataset.filter(:active => false).delete
|
66
|
+
dataset.filter('price < ?', 100).update(:active => true)
|
67
|
+
|
68
|
+
== Datasets are Enumerable
|
69
|
+
|
70
|
+
dataset.map {|r| r[:name]}
|
71
|
+
dataset.map(:name) # same effect as above
|
72
|
+
|
73
|
+
dataset.inject {|sum, r| sum + r[:value]}
|
74
|
+
|
75
|
+
== Filtering (see also doc/dataset_filtering.rdoc)
|
76
|
+
|
77
|
+
dataset.filter(:name => 'abc')
|
78
|
+
dataset.filter('name = ?', 'abc')
|
79
|
+
dataset.filter(:value > 100)
|
80
|
+
dataset.exclude(:value <= 100)
|
81
|
+
|
82
|
+
dataset.filter(:value => 50..100)
|
83
|
+
dataset.where((:value >= 50) & (:value <= 100))
|
84
|
+
|
85
|
+
dataset.where('value IN ?', [50,75,100])
|
86
|
+
|
87
|
+
# Get the first record that matches a condition
|
88
|
+
dataset[:name => 'abc'] # Same as:
|
89
|
+
dataset.filter(:name => 'abc').first
|
90
|
+
|
91
|
+
# Filter using a subquery
|
92
|
+
dataset.filter('price > ?', dataset.select('AVG(price) + 100'))
|
93
|
+
|
94
|
+
=== Advanced filtering using ruby expressions without blocks
|
95
|
+
|
96
|
+
Available as of Sequel 2.0:
|
97
|
+
|
98
|
+
DB[:items].filter(:price < 100).sql
|
99
|
+
#=> "SELECT * FROM items WHERE (price < 100)"
|
100
|
+
|
101
|
+
DB[:items].filter(:name.like('AL%')).sql
|
102
|
+
#=> "SELECT * FROM items WHERE (name LIKE 'AL%')"
|
103
|
+
|
104
|
+
There's support for nested expressions with AND, OR and NOT:
|
105
|
+
|
106
|
+
DB[:items].filter((:x > 5) & (:y > 10)).sql
|
107
|
+
#=> "SELECT * FROM items WHERE ((x > 5) AND (y > 10))"
|
108
|
+
|
109
|
+
DB[:items].filter({:x => 1, :y => 2}.sql_or & ~{:z => 3}).sql
|
110
|
+
#=> "SELECT * FROM items WHERE (((x = 1) OR (y = 2)) AND (z != 3))"
|
111
|
+
|
112
|
+
You can use arithmetic operators and specify SQL functions:
|
113
|
+
|
114
|
+
DB[:items].filter((:x + :y) > :z).sql
|
115
|
+
#=> "SELECT * FROM items WHERE ((x + y) > z)"
|
116
|
+
|
117
|
+
DB[:items].filter(:price < :AVG[:price] + 100).sql
|
118
|
+
#=> "SELECT * FROM items WHERE (price < (AVG(price) + 100))"
|
119
|
+
|
120
|
+
== Ordering
|
121
|
+
|
122
|
+
dataset.order(:kind)
|
123
|
+
dataset.reverse_order(:kind)
|
124
|
+
dataset.order(:kind.desc, :name)
|
125
|
+
|
126
|
+
== Row ranges
|
127
|
+
|
128
|
+
dataset.limit(30) # LIMIT 30
|
129
|
+
dataset.limit(30, 10) # LIMIT 30 OFFSET 10
|
130
|
+
|
131
|
+
== Pagination
|
132
|
+
|
133
|
+
paginated = dataset.paginate(1, 10) # first page, 10 rows per page
|
134
|
+
paginated.page_count #=> number of pages in dataset
|
135
|
+
paginated.current_page #=> 1
|
136
|
+
paginated.next_page #=> next page number or nil
|
137
|
+
paginated.prev_page #=> previous page number or nil
|
138
|
+
paginated.first_page? #=> true if page number = 1
|
139
|
+
paginated.last_page? #=> true if page number = page_count
|
140
|
+
|
141
|
+
== Joins
|
142
|
+
|
143
|
+
DB[:items].left_outer_join(:categories, :id => :category_id).sql #=>
|
144
|
+
"SELECT * FROM items LEFT OUTER JOIN categories ON categories.id = items.category_id"
|
145
|
+
|
146
|
+
== Summarizing
|
147
|
+
|
148
|
+
dataset.count #=> record count
|
149
|
+
dataset.max(:price)
|
150
|
+
dataset.min(:price)
|
151
|
+
dataset.avg(:price)
|
152
|
+
dataset.sum(:stock)
|
153
|
+
|
154
|
+
dataset.group(:category).select(:category, :AVG[:price])
|
155
|
+
|
156
|
+
== SQL Functions / Literals
|
157
|
+
|
158
|
+
dataset.update(:updated_at => :NOW[])
|
159
|
+
dataset.update(:updated_at => 'NOW()'.lit)
|
160
|
+
|
161
|
+
dataset.update(:updated_at => "DateValue('1/1/2001')".lit)
|
162
|
+
dataset.update(:updated_at => :DateValue['1/1/2001'])
|
163
|
+
|
164
|
+
== Schema Manipulation
|
165
|
+
|
166
|
+
DB.create_table :items do
|
167
|
+
primary_key :id
|
168
|
+
text :name, :unique => true, :null => false
|
169
|
+
boolean :active, :default => true
|
170
|
+
foreign_key :category_id, :categories
|
171
|
+
|
172
|
+
index :grade
|
173
|
+
end
|
174
|
+
|
175
|
+
DB.drop_table :items
|
176
|
+
|
177
|
+
DB.create_table :test do
|
178
|
+
varchar :zipcode, :size => 10
|
179
|
+
enum :system, :elements => ['mac', 'linux', 'windows']
|
180
|
+
end
|
181
|
+
|
182
|
+
== Aliasing
|
183
|
+
|
184
|
+
DB[:items].select(:name.as(:item_name))
|
185
|
+
DB[:items].select(:name => :item_name)
|
186
|
+
DB[:items].select(:name___item_name)
|
187
|
+
DB[:items___items_table].select(:items_table__name___item_name)
|
188
|
+
# => "SELECT items_table.name AS item_name FROM items AS items_table"
|
189
|
+
|
190
|
+
== Transactions
|
191
|
+
|
192
|
+
DB.transaction do
|
193
|
+
dataset << {:first_name => 'Inigo', :last_name => 'Montoya'}
|
194
|
+
dataset << {:first_name => 'Farm', :last_name => 'Boy'}
|
195
|
+
end # Either both are inserted or neither are inserted
|
196
|
+
|
197
|
+
Database#transaction is re-entrant:
|
198
|
+
|
199
|
+
DB.transaction do # BEGIN issued only here
|
200
|
+
DB.transaction
|
201
|
+
dataset << {:first_name => 'Inigo', :last_name => 'Montoya'}
|
202
|
+
end
|
203
|
+
end # COMMIT issued only here
|
204
|
+
|
205
|
+
Transactions are aborted if an error is raised:
|
206
|
+
|
207
|
+
DB.transaction do
|
208
|
+
raise "some error occurred"
|
209
|
+
end # ROLLBACK issued and the error is re-raised
|
210
|
+
|
211
|
+
Transactions can also be aborted by raising Sequel::Error::Rollback:
|
212
|
+
|
213
|
+
DB.transaction do
|
214
|
+
raise(Sequel::Error::Rollback) if something_bad_happened
|
215
|
+
end # ROLLBACK issued and no error raised
|
216
|
+
|
217
|
+
Miscellaneous:
|
218
|
+
|
219
|
+
dataset.sql #=> "SELECT * FROM items"
|
220
|
+
dataset.delete_sql #=> "DELETE FROM items"
|
221
|
+
dataset.where(:name => 'sequel').exists #=> "EXISTS ( SELECT 1 FROM items WHERE name = 'sequel' )"
|
222
|
+
dataset.print #=> pretty table print to $stdout
|
223
|
+
dataset.columns #=> array of columns in the result set, does a SELECT
|
224
|
+
DB.schema_for_table(:items) => [[:id, {:type=>:integer, ...}], [:name, {:type=>:string, ...}], ...]
|
225
|
+
# Works on PostgreSQL, MySQL, SQLite, and possibly elsewhere
|
@@ -0,0 +1,257 @@
|
|
1
|
+
= Dataset Filtering
|
2
|
+
|
3
|
+
Sequel offers unparalleled flexibility when it comes to filtering records. You can specify your conditions as a custom string, as a string with parameters, as a hash of values to compare against, or as ruby code that Sequel translates into SQL expressions.
|
4
|
+
|
5
|
+
== Filtering using a custom filter string
|
6
|
+
|
7
|
+
If you do not wish to lose control over your SQL WHERE clauses, you can just supply it to the dataset's #filter method:
|
8
|
+
|
9
|
+
items.filter('x < 10').sql
|
10
|
+
#=> "SELECT * FROM items WHERE x < 10"
|
11
|
+
|
12
|
+
In order to prevent SQL injection, you can replace literal values with question marks and supply the values as additional arguments:
|
13
|
+
|
14
|
+
items.filter('category = ?', 'ruby').sql
|
15
|
+
#=> "SELECT * FROM items WHERE category = 'ruby'"
|
16
|
+
|
17
|
+
== An aside: column references in Sequel
|
18
|
+
|
19
|
+
Sequel expects column names to be specified using symbols. In addition, tuples always use symbols as their keys. This allows you to freely mix literal values and column references. For example, the two following lines produce equivalent SQL:
|
20
|
+
|
21
|
+
items.filter(:x => 1) #=> "SELECT * FROM items WHERE (x = 1)"
|
22
|
+
items.filter(1 => :x) #=> "SELECT * FROM items WHERE (1 = x)"
|
23
|
+
|
24
|
+
=== Qualifying column names
|
25
|
+
|
26
|
+
Column references can be qualified by using the double underscore special notation :table__column:
|
27
|
+
|
28
|
+
items.literal(:items__price) #=> "items.price"
|
29
|
+
|
30
|
+
=== Column aliases
|
31
|
+
|
32
|
+
You can also alias columns by using the triple undersecore special notation :column___alias or :table__column___alias:
|
33
|
+
|
34
|
+
items.literal(:price___p) #=> "price AS p"
|
35
|
+
items.literal(:items__price___p) #=> "items.price AS p"
|
36
|
+
|
37
|
+
Another way to alias columns is to use the #AS method:
|
38
|
+
|
39
|
+
items.literal(:price.as(:p)) #=> "price AS p"
|
40
|
+
|
41
|
+
=== Specifying SQL functions
|
42
|
+
|
43
|
+
Sequel also allows you to specify functions by using the Symbol#[] method:
|
44
|
+
|
45
|
+
items.literal(:avg[:price]) #=> "avg(price)"
|
46
|
+
|
47
|
+
== Filtering using a hash
|
48
|
+
|
49
|
+
If you just need to compare records against values, you can supply a hash:
|
50
|
+
|
51
|
+
items.filter(:category => 'ruby').sql
|
52
|
+
#=> "SELECT * FROM items WHERE (category = 'ruby')"
|
53
|
+
|
54
|
+
Sequel can check for null values:
|
55
|
+
|
56
|
+
items.filter(:category => nil).sql
|
57
|
+
#=> "SELECT * FROM items WHERE (category IS NULL)"
|
58
|
+
|
59
|
+
Or compare two columns:
|
60
|
+
|
61
|
+
items.filter(:x => :some_table__y).sql
|
62
|
+
#=> "SELECT * FROM items WHERE (x = some_table.y)"
|
63
|
+
|
64
|
+
And also compare against multiple values:
|
65
|
+
|
66
|
+
items.filter(:category => ['ruby', 'perl']).sql
|
67
|
+
#=> "SELECT * FROM items WHERE (category IN ('ruby', 'perl'))"
|
68
|
+
|
69
|
+
Ranges (both inclusive and exclusive) can also be used:
|
70
|
+
|
71
|
+
items.filter(:price => 100..200).sql
|
72
|
+
#=> "SELECT * FROM items WHERE (price >= 100 AND price <= 200)"
|
73
|
+
|
74
|
+
items.filter(:price => 100...200).sql
|
75
|
+
#=> "SELECT * FROM items WHERE (price >= 100 AND price < 200)"
|
76
|
+
|
77
|
+
== Filtering using expressions
|
78
|
+
|
79
|
+
New in Sequel 2.0 is the ability to use ruby expressions directly in the call to filter, without using a block:
|
80
|
+
|
81
|
+
items.filter(:price < 100).sql
|
82
|
+
#=> "SELECT * FROM items WHERE (price < 100)
|
83
|
+
|
84
|
+
This works for the standard inequality and arithmetic operators:
|
85
|
+
|
86
|
+
items.filter(:price + 100 < 200).sql
|
87
|
+
#=> "SELECT * FROM items WHERE ((price + 100) < 200)
|
88
|
+
|
89
|
+
items.filter(:price - 100 > 200).sql
|
90
|
+
#=> "SELECT * FROM items WHERE ((price - 100) > 200)
|
91
|
+
|
92
|
+
items.filter(:price * 100 <= 200).sql
|
93
|
+
#=> "SELECT * FROM items WHERE ((price * 100) <= 200)
|
94
|
+
|
95
|
+
items.filter(:price / 100 >= 200).sql
|
96
|
+
#=> "SELECT * FROM items WHERE ((price / 100) >= 200)
|
97
|
+
|
98
|
+
You use the overloaded bitwise and (&) and or (|) operators to combine expressions:
|
99
|
+
|
100
|
+
items.filter((:price + 100 < 200) & (:price * 100 <= 200)).sql
|
101
|
+
#=> "SELECT * FROM items WHERE (((price + 100) < 200) AND ((price * 100) <= 200))
|
102
|
+
|
103
|
+
items.filter((:price - 100 > 200) | (:price / 100 >= 200)).sql
|
104
|
+
#=> "SELECT * FROM items WHERE (((price - 100) > 200) OR ((price / 100) >= 200))
|
105
|
+
|
106
|
+
To filter by equality, you use the standard hash, which can be combined with other operators:
|
107
|
+
|
108
|
+
items.filter({:category => 'ruby'} & (:price + 100 < 200)).sql
|
109
|
+
#=> "SELECT * FROM items WHERE ((category = 'ruby') AND ((price + 100) < 200))"
|
110
|
+
|
111
|
+
This works with other hash values, such as arrays and ranges:
|
112
|
+
|
113
|
+
items.filter({:category => ['ruby', 'other']} | (:price - 100 > 200)).sql
|
114
|
+
#=> "SELECT * FROM items WHERE ((category IN ('ruby', 'other')) OR ((price - 100) <= 200))"
|
115
|
+
|
116
|
+
items.filter({:price == (100..200)} & :active).sql
|
117
|
+
#=> "SELECT * FROM items WHERE ((price >= 100 AND price <= 200) AND active)"
|
118
|
+
|
119
|
+
=== Negating conditions
|
120
|
+
|
121
|
+
You can use the negation operator (~) in most cases:
|
122
|
+
|
123
|
+
items.filter(~{:category => 'ruby'}).sql
|
124
|
+
#=> "SELECT * FROM items WHERE (category != 'ruby')"
|
125
|
+
|
126
|
+
items.filter {~:active}.sql
|
127
|
+
#=> "SELECT * FROM items WHERE NOT active"
|
128
|
+
|
129
|
+
items.filter(~(:price / 100 >= 200)).sql
|
130
|
+
#=> "SELECT * FROM items WHERE ((price / 100) < 200)
|
131
|
+
|
132
|
+
=== Comparing against column references
|
133
|
+
|
134
|
+
You can also compare against other columns:
|
135
|
+
|
136
|
+
items.filter(:credit > :debit).sql
|
137
|
+
#=> "SELECT * FROM items WHERE (credit > debit)
|
138
|
+
|
139
|
+
Or against SQL functions:
|
140
|
+
|
141
|
+
items.filter(:price < :max[:price] + 100).sql
|
142
|
+
#=> "SELECT * FROM items WHERE (price < (max(price) + 100))"
|
143
|
+
|
144
|
+
== String search functions
|
145
|
+
|
146
|
+
You can search SQL strings using the #like method:
|
147
|
+
|
148
|
+
items.filter(:name.like('Acme%').sql
|
149
|
+
#=> "SELECT * FROM items WHERE (name LIKE 'Acme%')"
|
150
|
+
|
151
|
+
You can specify a Regexp as a like argument, but this will probably only work
|
152
|
+
on PostgreSQL and MySQL:
|
153
|
+
|
154
|
+
items.filter(:name.like(/Acme.*/).sql
|
155
|
+
#=> "SELECT * FROM items WHERE (name ~ 'Acme.*')"
|
156
|
+
|
157
|
+
Like can also take more than one argument:
|
158
|
+
|
159
|
+
items.filter(:name.like('Acme%', /Beta.*/).sql
|
160
|
+
#=> "SELECT * FROM items WHERE ((name LIKE 'Acme%') OR (name ~ 'Beta.*'))"
|
161
|
+
|
162
|
+
== String concatenation
|
163
|
+
|
164
|
+
You can concatenate SQL strings using Array#sql_string_join:
|
165
|
+
|
166
|
+
items.filter([:name, :comment].sql_string_join.like('%acme%').sql
|
167
|
+
#=> "SELECT * FROM items WHERE ((name || comment) LIKE 'Acme%')"
|
168
|
+
|
169
|
+
Array#sql_string_join also takes a join argument:
|
170
|
+
|
171
|
+
items.filter([:name, :comment].sql_string_join(' ').like('%acme%').sql
|
172
|
+
#=> "SELECT * FROM items WHERE ((name || ' ' || comment) LIKE 'Acme%')"
|
173
|
+
|
174
|
+
== Filtering using expressions with blocks
|
175
|
+
|
176
|
+
Most SQL expressions that you can can create with expressions you can also express inside blocks. This was previously the only way to specify expressions using ruby code. Filtering with blocks requires that you install ParseTree, ruby2ruby, and their dependencies. It's slower than using the equivalent expression without a block, and the syntax inside the block is different in some cases. Because it requires ParseTree, it can only be used with MRI (Matz's Ruby Interpreter) 1.8.*, as ParseTree doesn't run on any other ruby implementation (blockless filters should work on other ruby implementations).
|
177
|
+
|
178
|
+
In general, filtering with a block should only be used with legacy code. While it is not officially deprecated, usage of blocks when filtering is discouraged.
|
179
|
+
|
180
|
+
To filter with a block, supply a block to filter with the appropriate ruby code:
|
181
|
+
|
182
|
+
items.filter{:price < 100}.sql
|
183
|
+
#=> "SELECT * FROM items WHERE (price < 100)
|
184
|
+
|
185
|
+
Sequel is smart enough to literalize values correctly, even if you compare against arrays or ranges:
|
186
|
+
|
187
|
+
items.filter{:category == 'ruby'}.sql
|
188
|
+
#=> "SELECT * FROM items WHERE (category = 'ruby')"
|
189
|
+
|
190
|
+
items.filter{:category == ['ruby', 'other']}.sql
|
191
|
+
#=> "SELECT * FROM items WHERE (category IN ('ruby', 'other'))"
|
192
|
+
|
193
|
+
items.filter{:price == (100..200)}.sql
|
194
|
+
#=> "SELECT * FROM items WHERE (price >= 100 AND price <= 200)"
|
195
|
+
|
196
|
+
=== Negating conditions
|
197
|
+
|
198
|
+
You can use the negation operator (!) anywhere:
|
199
|
+
|
200
|
+
items.filter{:category != 'ruby'}.sql
|
201
|
+
#=> "SELECT * FROM items WHERE NOT (category = 'ruby')"
|
202
|
+
|
203
|
+
items.filter{!:active}.sql
|
204
|
+
#=> "SELECT * FROM items WHERE (active = 'f')"
|
205
|
+
|
206
|
+
=== Comparing against column references
|
207
|
+
|
208
|
+
You can also compare against other columns:
|
209
|
+
|
210
|
+
items.filter{:credit > :debit}.sql
|
211
|
+
#=> "SELECT * FROM items WHERE (credit > debit)
|
212
|
+
|
213
|
+
Or against SQL functions:
|
214
|
+
|
215
|
+
items.filter{:price < :max[:price] + 100}.sql
|
216
|
+
#=> "SELECT * FROM items WHERE (price < (max(price) + 100))"
|
217
|
+
|
218
|
+
=== Concatenating conditions with logical operators
|
219
|
+
|
220
|
+
Expressions can be nested and combined using logical operators:
|
221
|
+
|
222
|
+
items.filter{(:price < 100 && :age < 27) || :category == 'ruby'}.sql
|
223
|
+
#=> "SELECT * FROM items WHERE (((price < 100) AND (age < 27)) OR (category = 'ruby'))"
|
224
|
+
|
225
|
+
Another way to concatenate conditions is to specify each condition as a separate statement:
|
226
|
+
|
227
|
+
items.filter do
|
228
|
+
:price < 100
|
229
|
+
:category == 'ruby'
|
230
|
+
end.sql
|
231
|
+
#=> "SELECT * FROM items WHERE ((price < 100) AND (category = 'ruby'))"
|
232
|
+
|
233
|
+
=== Special methods
|
234
|
+
|
235
|
+
Inside blocks, Sequel recognizes the special methods nil/nil?, in/in? and like/like?:
|
236
|
+
|
237
|
+
items.filter{:price.nil?}.sql
|
238
|
+
#=> "SELECT * FROM items WHERE (price IS NULL)"
|
239
|
+
|
240
|
+
items.filter{:category.in ['ruby', 'other']}.sql
|
241
|
+
#=> "SELECT * FROM items WHERE (category IN ('ruby', 'other'))"
|
242
|
+
|
243
|
+
items.filter{:price.in 100..200}.sql
|
244
|
+
#=> "SELECT * FROM items WHERE (price >= 100 AND price <= 200)"
|
245
|
+
|
246
|
+
items.filter{:category.like 'ruby%'}.sql
|
247
|
+
#=> "SELECT * FROM items WHERE (category LIKE 'ruby%')"
|
248
|
+
|
249
|
+
== Filtering using sub-queries
|
250
|
+
|
251
|
+
One of the best features of Sequel is the ability to use datasets as sub-queries. Sub-queries can be very useful for filtering records, and many times provide a simpler alternative to table joins. Sub-queries can be used in all forms of filters:
|
252
|
+
|
253
|
+
refs = consumer_refs.filter(:logged_in => true).select(:consumer_id)
|
254
|
+
consumers.filter(:id => refs).sql
|
255
|
+
#=> "SELECT * FROM consumers WHERE (id IN (SELECT consumer_id FROM consumer_refs WHERE (logged_in = 't')))"
|
256
|
+
|
257
|
+
Note that if you compare against a sub-query, you must select a single column in the sub-query.
|
@@ -18,7 +18,7 @@ module Sequel
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def execute(sql)
|
21
|
-
|
21
|
+
log_info(sql)
|
22
22
|
@pool.hold {|conn| conn.exec(sql)}
|
23
23
|
end
|
24
24
|
|
@@ -30,6 +30,8 @@ module Sequel
|
|
30
30
|
case v
|
31
31
|
when Time
|
32
32
|
literal(v.iso8601)
|
33
|
+
when Date, DateTime
|
34
|
+
literal(v.to_s)
|
33
35
|
else
|
34
36
|
super
|
35
37
|
end
|
@@ -65,4 +67,4 @@ module Sequel
|
|
65
67
|
end
|
66
68
|
end
|
67
69
|
end
|
68
|
-
end
|
70
|
+
end
|
@@ -36,7 +36,7 @@ module Sequel
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def execute(sql)
|
39
|
-
|
39
|
+
log_info(sql)
|
40
40
|
@pool.hold {|conn| conn.Execute(sql)}
|
41
41
|
end
|
42
42
|
|
@@ -48,6 +48,8 @@ module Sequel
|
|
48
48
|
case v
|
49
49
|
when Time
|
50
50
|
literal(v.iso8601)
|
51
|
+
when Date, DateTime
|
52
|
+
literal(v.to_s)
|
51
53
|
else
|
52
54
|
super
|
53
55
|
end
|
@@ -54,7 +54,7 @@ module Sequel
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def execute(sql, &block)
|
57
|
-
|
57
|
+
log_info(sql)
|
58
58
|
@pool.hold do |conn|
|
59
59
|
rc, sth = SQLAllocHandle(SQL_HANDLE_STMT, @handle)
|
60
60
|
check_error(rc, "Could not allocate statement")
|
@@ -82,6 +82,8 @@ module Sequel
|
|
82
82
|
case v
|
83
83
|
when Time
|
84
84
|
literal(v.iso8601)
|
85
|
+
when Date, DateTime
|
86
|
+
literal(v.to_s)
|
85
87
|
else
|
86
88
|
super
|
87
89
|
end
|
@@ -155,4 +157,4 @@ module Sequel
|
|
155
157
|
end
|
156
158
|
end
|
157
159
|
end
|
158
|
-
end
|
160
|
+
end
|