viking-sequel 3.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3134 -0
- data/COPYING +19 -0
- data/README.rdoc +723 -0
- data/Rakefile +193 -0
- data/bin/sequel +196 -0
- data/doc/advanced_associations.rdoc +644 -0
- data/doc/cheat_sheet.rdoc +218 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/dataset_filtering.rdoc +158 -0
- data/doc/opening_databases.rdoc +296 -0
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/reflection.rdoc +84 -0
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/doc/release_notes/3.10.0.txt +286 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/doc/release_notes/3.4.0.txt +325 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/release_notes/3.8.0.txt +151 -0
- data/doc/release_notes/3.9.0.txt +233 -0
- data/doc/schema.rdoc +36 -0
- data/doc/sharding.rdoc +113 -0
- data/doc/virtual_rows.rdoc +205 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +90 -0
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/amalgalite.rb +176 -0
- data/lib/sequel/adapters/db2.rb +139 -0
- data/lib/sequel/adapters/dbi.rb +113 -0
- data/lib/sequel/adapters/do.rb +188 -0
- data/lib/sequel/adapters/do/mysql.rb +49 -0
- data/lib/sequel/adapters/do/postgres.rb +91 -0
- data/lib/sequel/adapters/do/sqlite.rb +40 -0
- data/lib/sequel/adapters/firebird.rb +283 -0
- data/lib/sequel/adapters/informix.rb +77 -0
- data/lib/sequel/adapters/jdbc.rb +587 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/h2.rb +133 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
- data/lib/sequel/adapters/mysql.rb +421 -0
- data/lib/sequel/adapters/odbc.rb +143 -0
- data/lib/sequel/adapters/odbc/mssql.rb +42 -0
- data/lib/sequel/adapters/openbase.rb +64 -0
- data/lib/sequel/adapters/oracle.rb +131 -0
- data/lib/sequel/adapters/postgres.rb +504 -0
- data/lib/sequel/adapters/shared/mssql.rb +490 -0
- data/lib/sequel/adapters/shared/mysql.rb +498 -0
- data/lib/sequel/adapters/shared/oracle.rb +195 -0
- data/lib/sequel/adapters/shared/postgres.rb +830 -0
- data/lib/sequel/adapters/shared/progress.rb +44 -0
- data/lib/sequel/adapters/shared/sqlite.rb +389 -0
- data/lib/sequel/adapters/sqlite.rb +224 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
- data/lib/sequel/connection_pool.rb +99 -0
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +293 -0
- data/lib/sequel/core_sql.rb +241 -0
- data/lib/sequel/database.rb +1079 -0
- data/lib/sequel/database/schema_generator.rb +327 -0
- data/lib/sequel/database/schema_methods.rb +203 -0
- data/lib/sequel/database/schema_sql.rb +320 -0
- data/lib/sequel/dataset.rb +32 -0
- data/lib/sequel/dataset/actions.rb +441 -0
- data/lib/sequel/dataset/features.rb +86 -0
- data/lib/sequel/dataset/graph.rb +254 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +227 -0
- data/lib/sequel/dataset/query.rb +709 -0
- data/lib/sequel/dataset/sql.rb +996 -0
- data/lib/sequel/exceptions.rb +51 -0
- data/lib/sequel/extensions/blank.rb +43 -0
- data/lib/sequel/extensions/inflector.rb +242 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/migration.rb +239 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/pagination.rb +100 -0
- data/lib/sequel/extensions/pretty_table.rb +82 -0
- data/lib/sequel/extensions/query.rb +52 -0
- data/lib/sequel/extensions/schema_dumper.rb +271 -0
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +46 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/metaprogramming.rb +9 -0
- data/lib/sequel/model.rb +120 -0
- data/lib/sequel/model/associations.rb +1514 -0
- data/lib/sequel/model/base.rb +1069 -0
- data/lib/sequel/model/default_inflections.rb +45 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/sequel/model/exceptions.rb +21 -0
- data/lib/sequel/model/inflections.rb +162 -0
- data/lib/sequel/model/plugins.rb +70 -0
- data/lib/sequel/plugins/active_model.rb +59 -0
- data/lib/sequel/plugins/association_dependencies.rb +103 -0
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/boolean_readers.rb +53 -0
- data/lib/sequel/plugins/caching.rb +141 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/force_encoding.rb +72 -0
- data/lib/sequel/plugins/hook_class_methods.rb +126 -0
- data/lib/sequel/plugins/identity_map.rb +116 -0
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/instance_hooks.rb +57 -0
- data/lib/sequel/plugins/lazy_attributes.rb +77 -0
- data/lib/sequel/plugins/many_through_many.rb +208 -0
- data/lib/sequel/plugins/nested_attributes.rb +206 -0
- data/lib/sequel/plugins/optimistic_locking.rb +81 -0
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/schema.rb +66 -0
- data/lib/sequel/plugins/serialization.rb +166 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/timestamps.rb +87 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +72 -0
- data/lib/sequel/plugins/validation_class_methods.rb +405 -0
- data/lib/sequel/plugins/validation_helpers.rb +223 -0
- data/lib/sequel/sql.rb +1020 -0
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +12 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/firebird_spec.rb +407 -0
- data/spec/adapters/informix_spec.rb +97 -0
- data/spec/adapters/mssql_spec.rb +403 -0
- data/spec/adapters/mysql_spec.rb +1019 -0
- data/spec/adapters/oracle_spec.rb +286 -0
- data/spec/adapters/postgres_spec.rb +969 -0
- data/spec/adapters/spec_helper.rb +51 -0
- data/spec/adapters/sqlite_spec.rb +432 -0
- data/spec/core/connection_pool_spec.rb +808 -0
- data/spec/core/core_sql_spec.rb +417 -0
- data/spec/core/database_spec.rb +1662 -0
- data/spec/core/dataset_spec.rb +3827 -0
- data/spec/core/expression_filters_spec.rb +595 -0
- data/spec/core/object_graph_spec.rb +296 -0
- data/spec/core/schema_generator_spec.rb +159 -0
- data/spec/core/schema_spec.rb +830 -0
- data/spec/core/spec_helper.rb +56 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/active_model_spec.rb +76 -0
- data/spec/extensions/association_dependencies_spec.rb +127 -0
- data/spec/extensions/association_proxies_spec.rb +50 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/boolean_readers_spec.rb +92 -0
- data/spec/extensions/caching_spec.rb +250 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/force_encoding_spec.rb +117 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/identity_map_spec.rb +202 -0
- data/spec/extensions/inflector_spec.rb +181 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/instance_hooks_spec.rb +133 -0
- data/spec/extensions/lazy_attributes_spec.rb +153 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +884 -0
- data/spec/extensions/migration_spec.rb +332 -0
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +396 -0
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +357 -0
- data/spec/extensions/schema_spec.rb +127 -0
- data/spec/extensions/serialization_spec.rb +209 -0
- data/spec/extensions/single_table_inheritance_spec.rb +96 -0
- data/spec/extensions/spec_helper.rb +91 -0
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/timestamps_spec.rb +150 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +69 -0
- data/spec/extensions/validation_class_methods_spec.rb +984 -0
- data/spec/extensions/validation_helpers_spec.rb +438 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/database_test.rb +26 -0
- data/spec/integration/dataset_test.rb +963 -0
- data/spec/integration/eager_loader_test.rb +734 -0
- data/spec/integration/model_test.rb +130 -0
- data/spec/integration/plugin_test.rb +814 -0
- data/spec/integration/prepared_statement_test.rb +213 -0
- data/spec/integration/schema_test.rb +361 -0
- data/spec/integration/spec_helper.rb +73 -0
- data/spec/integration/timezone_test.rb +55 -0
- data/spec/integration/transaction_test.rb +122 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +175 -0
- data/spec/model/associations_spec.rb +2633 -0
- data/spec/model/base_spec.rb +418 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1391 -0
- data/spec/model/hooks_spec.rb +240 -0
- data/spec/model/inflector_spec.rb +26 -0
- data/spec/model/model_spec.rb +593 -0
- data/spec/model/plugins_spec.rb +236 -0
- data/spec/model/record_spec.rb +1500 -0
- data/spec/model/spec_helper.rb +97 -0
- data/spec/model/validations_spec.rb +153 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +346 -0
@@ -0,0 +1,218 @@
|
|
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.connect('postgres://user:password@localhost/my_db')
|
10
|
+
DB = Sequel.postgres('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 memory.
|
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.run "CREATE TABLE users (name VARCHAR(255) NOT NULL, age INT(3) NOT NULL)"
|
29
|
+
dataset = DB["SELECT age FROM users WHERE name = ?", name]
|
30
|
+
dataset.map(:age)
|
31
|
+
DB.fetch("SELECT name FROM users") do |row|
|
32
|
+
p r[:name]
|
33
|
+
end
|
34
|
+
|
35
|
+
== Create a dataset
|
36
|
+
|
37
|
+
dataset = DB[:items]
|
38
|
+
dataset = DB.from(:items)
|
39
|
+
|
40
|
+
== Most dataset methods are chainable
|
41
|
+
|
42
|
+
dataset = DB[:managers].where(:salary => 5000..10000).order(:name, :department)
|
43
|
+
|
44
|
+
== Insert rows
|
45
|
+
|
46
|
+
dataset.insert(:name => 'Sharon', :grade => 50)
|
47
|
+
|
48
|
+
== Retrieve rows
|
49
|
+
|
50
|
+
dataset.each{|r| p r}
|
51
|
+
dataset.all #=> [{...}, {...}, ...]
|
52
|
+
dataset.first
|
53
|
+
|
54
|
+
== Update/Delete rows
|
55
|
+
|
56
|
+
dataset.filter(~:active).delete
|
57
|
+
dataset.filter('price < ?', 100).update(:active => true)
|
58
|
+
|
59
|
+
== Datasets are Enumerable
|
60
|
+
|
61
|
+
dataset.map{|r| r[:name]}
|
62
|
+
dataset.map(:name) # same as above
|
63
|
+
|
64
|
+
dataset.inject(0){|sum, r| sum + r[:value]}
|
65
|
+
dataset.sum(:value) # same as above
|
66
|
+
|
67
|
+
== Filtering (see also doc/dataset_filtering.rdoc)
|
68
|
+
|
69
|
+
dataset.filter(:name => 'abc')
|
70
|
+
dataset.filter('name = ?', 'abc')
|
71
|
+
|
72
|
+
dataset.filter{|o| o.value > 100}
|
73
|
+
dataset.exclude{|o| o.value <= 100}
|
74
|
+
|
75
|
+
dataset.filter(:value => 50..100)
|
76
|
+
dataset.where{|o| (o.value >= 50) & (o.value <= 100)}
|
77
|
+
|
78
|
+
dataset.where('value IN ?', [50,75,100])
|
79
|
+
dataset.where(:value=>[50,75,100])
|
80
|
+
|
81
|
+
dataset.filter(:name => 'abc').first
|
82
|
+
dataset[:name => 'abc']
|
83
|
+
|
84
|
+
dataset.where('price > (SELECT avg(price) + 100 FROM table)')
|
85
|
+
dataset.filter{|o| o.price > dataset.select(o.avg(price) + 100)}
|
86
|
+
|
87
|
+
=== Advanced filtering using ruby expressions
|
88
|
+
|
89
|
+
DB[:items].filter{|o| o.price < 100}.sql
|
90
|
+
#=> "SELECT * FROM items WHERE (price < 100)"
|
91
|
+
|
92
|
+
DB[:items].filter(:name.like('AL%')).sql
|
93
|
+
#=> "SELECT * FROM items WHERE (name LIKE 'AL%')"
|
94
|
+
|
95
|
+
There's support for nested expressions with AND, OR and NOT:
|
96
|
+
|
97
|
+
DB[:items].filter{|o| (o.x > 5) & (o.y > 10)}.sql
|
98
|
+
#=> "SELECT * FROM items WHERE ((x > 5) AND (y > 10))"
|
99
|
+
|
100
|
+
DB[:items].filter({:x => 1, :y => 2}.sql_or & ~{:z => 3}).sql
|
101
|
+
#=> "SELECT * FROM items WHERE (((x = 1) OR (y = 2)) AND (z != 3))"
|
102
|
+
|
103
|
+
You can use arithmetic operators and specify SQL functions:
|
104
|
+
|
105
|
+
DB[:items].filter((:x + :y) > :z).sql
|
106
|
+
#=> "SELECT * FROM items WHERE ((x + y) > z)"
|
107
|
+
|
108
|
+
DB[:items].filter{|o| :price - 100 < o.avg(:price)}.sql
|
109
|
+
#=> "SELECT * FROM items WHERE ((price - 100) < avg(price))"
|
110
|
+
|
111
|
+
== Ordering
|
112
|
+
|
113
|
+
dataset.order(:kind)
|
114
|
+
dataset.reverse_order(:kind)
|
115
|
+
dataset.order(:kind.desc, :name)
|
116
|
+
|
117
|
+
== Row ranges
|
118
|
+
|
119
|
+
dataset.limit(30) # LIMIT 30
|
120
|
+
dataset.limit(30, 10) # LIMIT 30 OFFSET 10
|
121
|
+
|
122
|
+
== Joins
|
123
|
+
|
124
|
+
DB[:items].left_outer_join(:categories, :id => :category_id).sql
|
125
|
+
#=> "SELECT * FROM items LEFT OUTER JOIN categories ON categories.id = items.category_id"
|
126
|
+
|
127
|
+
DB[:items].join(:categories, :id => :category_id).join(:groups, :id => :items__group_id)
|
128
|
+
#=> "SELECT * FROM items INNER JOIN categories ON categories.id = items.category_id INNER JOIN groups ON groups.id = items.group_id"
|
129
|
+
|
130
|
+
== Summarizing
|
131
|
+
|
132
|
+
dataset.count #=> record count
|
133
|
+
dataset.max(:price)
|
134
|
+
dataset.min(:price)
|
135
|
+
dataset.avg(:price)
|
136
|
+
dataset.sum(:stock)
|
137
|
+
|
138
|
+
dataset.group(:category).select(:category, :AVG.sql_function(:price))
|
139
|
+
|
140
|
+
== SQL Functions / Literals
|
141
|
+
|
142
|
+
dataset.update(:updated_at => :NOW.sql_function)
|
143
|
+
dataset.update(:updated_at => 'NOW()'.lit)
|
144
|
+
|
145
|
+
dataset.update(:updated_at => "DateValue('1/1/2001')".lit)
|
146
|
+
dataset.update(:updated_at => :DateValue.sql_function('1/1/2001'))
|
147
|
+
|
148
|
+
== Schema Manipulation
|
149
|
+
|
150
|
+
DB.create_table :items do
|
151
|
+
primary_key :id
|
152
|
+
String :name, :unique => true, :null => false
|
153
|
+
boolean :active, :default => true
|
154
|
+
foreign_key :category_id, :categories
|
155
|
+
Time :created_at
|
156
|
+
|
157
|
+
index :grade
|
158
|
+
end
|
159
|
+
|
160
|
+
DB.drop_table :items
|
161
|
+
|
162
|
+
DB.create_table :test do
|
163
|
+
String :zipcode
|
164
|
+
enum :system, :elements => ['mac', 'linux', 'windows']
|
165
|
+
end
|
166
|
+
|
167
|
+
== Aliasing
|
168
|
+
|
169
|
+
DB[:items].select(:name.as(:item_name))
|
170
|
+
DB[:items].select(:name___item_name)
|
171
|
+
DB[:items___items_table].select(:items_table__name___item_name)
|
172
|
+
# => "SELECT items_table.name AS item_name FROM items AS items_table"
|
173
|
+
|
174
|
+
== Transactions
|
175
|
+
|
176
|
+
DB.transaction do
|
177
|
+
dataset.insert(:first_name => 'Inigo', :last_name => 'Montoya')
|
178
|
+
dataset.insert(:first_name => 'Farm', :last_name => 'Boy')
|
179
|
+
end # Either both are inserted or neither are inserted
|
180
|
+
|
181
|
+
Database#transaction is re-entrant:
|
182
|
+
|
183
|
+
DB.transaction do # BEGIN issued only here
|
184
|
+
DB.transaction
|
185
|
+
dataset << {:first_name => 'Inigo', :last_name => 'Montoya'}
|
186
|
+
end
|
187
|
+
end # COMMIT issued only here
|
188
|
+
|
189
|
+
Transactions are aborted if an error is raised:
|
190
|
+
|
191
|
+
DB.transaction do
|
192
|
+
raise "some error occurred"
|
193
|
+
end # ROLLBACK issued and the error is re-raised
|
194
|
+
|
195
|
+
Transactions can also be aborted by raising Sequel::Rollback:
|
196
|
+
|
197
|
+
DB.transaction do
|
198
|
+
raise(Sequel::Rollback) if something_bad_happened
|
199
|
+
end # ROLLBACK issued and no error raised
|
200
|
+
|
201
|
+
Savepoints can be used if the database supports it:
|
202
|
+
|
203
|
+
DB.transaction do
|
204
|
+
dataset << {:first_name => 'Farm', :last_name => 'Boy'} # Inserted
|
205
|
+
DB.transaction(:savepoint=>true) # This savepoint is rolled back
|
206
|
+
dataset << {:first_name => 'Inigo', :last_name => 'Montoya'} # Not inserted
|
207
|
+
raise(Sequel::Rollback) if something_bad_happened
|
208
|
+
end
|
209
|
+
dataset << {:first_name => 'Prince', :last_name => 'Humperdink'} # Inserted
|
210
|
+
end
|
211
|
+
|
212
|
+
== Miscellaneous:
|
213
|
+
|
214
|
+
dataset.sql #=> "SELECT * FROM items"
|
215
|
+
dataset.delete_sql #=> "DELETE FROM items"
|
216
|
+
dataset.where(:name => 'sequel').exists #=> "EXISTS ( SELECT * FROM items WHERE name = 'sequel' )"
|
217
|
+
dataset.columns #=> array of columns in the result set, does a SELECT
|
218
|
+
DB.schema(:items) => [[:id, {:type=>:integer, ...}], [:name, {:type=>:string, ...}], ...]
|
@@ -0,0 +1,106 @@
|
|
1
|
+
= Dataset Basics
|
2
|
+
|
3
|
+
== Introduction
|
4
|
+
|
5
|
+
Datasets are probably the thing that separate Sequel from other database libraries. While most database libraries have specific support for updating all records or only a single record, Sequel's ability to represent SQL queries themselves as objects is what gives Sequel most of its power. However, if you haven't been exposed to the dataset concept before, it can be a little disorienting. This document aims to give a basic introduction to datasets and how to use them.
|
6
|
+
|
7
|
+
== What a Dataset Represents
|
8
|
+
|
9
|
+
A Dataset can be thought of representing one of two concepts:
|
10
|
+
|
11
|
+
* An SQL query
|
12
|
+
* An abstract set of rows and some related behavior
|
13
|
+
|
14
|
+
The first concept is more easily understood, so you should probably start with that assumption.
|
15
|
+
|
16
|
+
== Basics
|
17
|
+
|
18
|
+
The most basic dataset is the simple selection of all columns in a table:
|
19
|
+
|
20
|
+
ds = DB[:posts]
|
21
|
+
# SELECT * FROM posts
|
22
|
+
|
23
|
+
Here, DB represents your Sequel::Database object, and ds is your dataset, with the SQL query it represents below it.
|
24
|
+
|
25
|
+
One of the core dataset ideas that should be understood is that datasets use a functional style of modification, in which methods called on the dataset return modified copies of the dataset, they don't modify the dataset themselves:
|
26
|
+
|
27
|
+
ds2 = ds.filter(:id=>1)
|
28
|
+
ds2
|
29
|
+
# SELECT * FROM posts WHERE id = 1
|
30
|
+
ds
|
31
|
+
# SELECT * FROM posts
|
32
|
+
|
33
|
+
Note how ds itself is not modified. This is because ds.filter returns a modified copy of ds, instead of modifying ds itself. This makes using datasets both thread safe and easy to chain:
|
34
|
+
|
35
|
+
# Thread safe:
|
36
|
+
100.times do |i|
|
37
|
+
Thread.new do
|
38
|
+
ds.filter(:id=>i).first
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Easy to chain:
|
43
|
+
ds3 = ds.select(:id, :name).order(:name).filter{id < 100}
|
44
|
+
# SELECT id, name FROM posts WHERE id < 100 ORDER BY name
|
45
|
+
|
46
|
+
Thread safety you don't really need to worry about, but chainability is core to how Sequel is generally used. Almost all dataset methods that affect the SQL produced return modified copies of the receiving dataset.
|
47
|
+
|
48
|
+
Another important thing to realize is that dataset methods that return modified datasets do not execute the dataset's code on the database. Only dataset methods that return or yield results will execute the code on the database:
|
49
|
+
|
50
|
+
# No SQL queries sent:
|
51
|
+
ds3 = ds.select(:id, :name).order(:name).filter{id < 100}
|
52
|
+
|
53
|
+
# Until you call a method that returns results
|
54
|
+
results = ds3.all
|
55
|
+
|
56
|
+
One important consequence of this API style is that if you use a method chain that includes both methods that return modified copies and a method that executes the SQL, the method that executes the SQL should generally be the last method in the chain:
|
57
|
+
|
58
|
+
# Good
|
59
|
+
ds.select(:id, :name).order(:name).filter{id < 100}.all
|
60
|
+
|
61
|
+
# Bad
|
62
|
+
ds.all.select(:id, :name).order(:name).filter{id < 100}
|
63
|
+
|
64
|
+
This is because all will return an array of hashes, and select, order, and filter are dataset methods, not array methods.
|
65
|
+
|
66
|
+
== Methods
|
67
|
+
|
68
|
+
Most Dataset methods that users will use can be broken down into two types:
|
69
|
+
|
70
|
+
* Methods that return modified datasets
|
71
|
+
* Methods that execute code on the database
|
72
|
+
|
73
|
+
=== Methods that return modified datasets
|
74
|
+
|
75
|
+
Most dataset methods fall into this category, which can be further broken down by the clause they affect:
|
76
|
+
|
77
|
+
SELECT:: select, select_all, select_append, select_more
|
78
|
+
FROM:: from, from_self
|
79
|
+
JOIN:: join, join_table,
|
80
|
+
WHERE:: where, filter, exclude, and, or, grep, invert, unfiltered
|
81
|
+
GROUP:: group, group_by, group_and_count, ungrouped
|
82
|
+
HAVING:: having, filter, exclude, and, or, grep, invert, unfiltered
|
83
|
+
ORDER:: order, order_by, order_more, reverse, reverse_order, unordered
|
84
|
+
LIMIT:: limit
|
85
|
+
compounds:: union, intersect, except
|
86
|
+
locking:: for_update, lock_style
|
87
|
+
common table expressions:: with, with_recursive
|
88
|
+
qualification:: qualify, qualify_to, qualify_to_first_source
|
89
|
+
inserting:: set_defaults, set_overrides
|
90
|
+
other:: clone, distinct, naked, server, with_sql
|
91
|
+
|
92
|
+
=== Methods that execute code on the database
|
93
|
+
|
94
|
+
Most other dataset methods commonly used will execute the dataset's SQL on the database:
|
95
|
+
|
96
|
+
SELECT (All Records):: all, each, map, to_hash, select_map, select_order_map, select_hash, to_csv
|
97
|
+
SELECT (First Record):: first, last, get, [], empty?
|
98
|
+
SELECT (Aggregates):: count, avg, max, min, sum, range, interval
|
99
|
+
INSERT:: insert, <<, import, multi_insert, insert_multiple
|
100
|
+
UPDATE:: update, set, []=
|
101
|
+
DELETE:: delete
|
102
|
+
other:: columns, columns!, truncate
|
103
|
+
|
104
|
+
=== Other methods
|
105
|
+
|
106
|
+
See the Sequel::Dataset RDoc for other methods that are less commonly used.
|
@@ -0,0 +1,158 @@
|
|
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 wish to write your SQL by hand, 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
|
+
=== Specifying SQL functions
|
18
|
+
|
19
|
+
Sequel also allows you to specify functions by using the Symbol#sql_function method (and the Symbol#[] method on ruby 1.8):
|
20
|
+
|
21
|
+
items.literal(:avg.sql_function(:price)) #=> "avg(price)"
|
22
|
+
|
23
|
+
== Filtering using a hash
|
24
|
+
|
25
|
+
If you just need to compare records against values, you can supply a hash:
|
26
|
+
|
27
|
+
items.filter(:category => 'ruby').sql
|
28
|
+
#=> "SELECT * FROM items WHERE (category = 'ruby')"
|
29
|
+
|
30
|
+
Sequel can check for null values:
|
31
|
+
|
32
|
+
items.filter(:category => nil).sql
|
33
|
+
#=> "SELECT * FROM items WHERE (category IS NULL)"
|
34
|
+
|
35
|
+
Or compare two columns:
|
36
|
+
|
37
|
+
items.filter(:x => :some_table__y).sql
|
38
|
+
#=> "SELECT * FROM items WHERE (x = some_table.y)"
|
39
|
+
|
40
|
+
And also compare against multiple values:
|
41
|
+
|
42
|
+
items.filter(:category => ['ruby', 'perl']).sql
|
43
|
+
#=> "SELECT * FROM items WHERE (category IN ('ruby', 'perl'))"
|
44
|
+
|
45
|
+
Ranges (both inclusive and exclusive) can also be used:
|
46
|
+
|
47
|
+
items.filter(:price => 100..200).sql
|
48
|
+
#=> "SELECT * FROM items WHERE (price >= 100 AND price <= 200)"
|
49
|
+
|
50
|
+
items.filter(:price => 100...200).sql
|
51
|
+
#=> "SELECT * FROM items WHERE (price >= 100 AND price < 200)"
|
52
|
+
|
53
|
+
== Filtering using expressions
|
54
|
+
|
55
|
+
Sequel allows you to use ruby expressions directly in the call to filter:
|
56
|
+
|
57
|
+
items.filter(:price * 2 < 50).sql
|
58
|
+
#=> "SELECT * FROM items WHERE ((price * 2) < 50)
|
59
|
+
|
60
|
+
This works for the standard inequality and arithmetic operators (though you can't use the inequality operators directly on a symbol in ruby 1.9):
|
61
|
+
|
62
|
+
items.filter(:price + 100 < 200).sql
|
63
|
+
#=> "SELECT * FROM items WHERE ((price + 100) < 200)
|
64
|
+
|
65
|
+
items.filter(:price - 100 > 200).sql
|
66
|
+
#=> "SELECT * FROM items WHERE ((price - 100) > 200)
|
67
|
+
|
68
|
+
items.filter(:price * 100 <= 200).sql
|
69
|
+
#=> "SELECT * FROM items WHERE ((price * 100) <= 200)
|
70
|
+
|
71
|
+
items.filter(:price / 100 >= 200).sql
|
72
|
+
#=> "SELECT * FROM items WHERE ((price / 100) >= 200)
|
73
|
+
|
74
|
+
You use the overloaded bitwise and (&) and or (|) operators to combine expressions:
|
75
|
+
|
76
|
+
items.filter((:price + 100 < 200) & (:price * 100 <= 200)).sql
|
77
|
+
#=> "SELECT * FROM items WHERE (((price + 100) < 200) AND ((price * 100) <= 200))
|
78
|
+
|
79
|
+
items.filter((:price - 100 > 200) | (:price / 100 >= 200)).sql
|
80
|
+
#=> "SELECT * FROM items WHERE (((price - 100) > 200) OR ((price / 100) >= 200))
|
81
|
+
|
82
|
+
To filter by equality, you use the standard hash, which can be combined with other operators:
|
83
|
+
|
84
|
+
items.filter({:category => 'ruby'} & (:price + 100 < 200)).sql
|
85
|
+
#=> "SELECT * FROM items WHERE ((category = 'ruby') AND ((price + 100) < 200))"
|
86
|
+
|
87
|
+
This works with other hash values, such as arrays and ranges:
|
88
|
+
|
89
|
+
items.filter({:category => ['ruby', 'other']} | (:price - 100 > 200)).sql
|
90
|
+
#=> "SELECT * FROM items WHERE ((category IN ('ruby', 'other')) OR ((price - 100) <= 200))"
|
91
|
+
|
92
|
+
items.filter({:price => (100..200)} & :active).sql
|
93
|
+
#=> "SELECT * FROM items WHERE ((price >= 100 AND price <= 200) AND active)"
|
94
|
+
|
95
|
+
=== Negating conditions
|
96
|
+
|
97
|
+
You can use the negation operator (~) in most cases:
|
98
|
+
|
99
|
+
items.filter(~{:category => 'ruby'}).sql
|
100
|
+
#=> "SELECT * FROM items WHERE (category != 'ruby')"
|
101
|
+
|
102
|
+
items.filter{~:active}.sql
|
103
|
+
#=> "SELECT * FROM items WHERE NOT active"
|
104
|
+
|
105
|
+
items.filter(~(:price / 100 >= 200)).sql
|
106
|
+
#=> "SELECT * FROM items WHERE ((price / 100) < 200)
|
107
|
+
|
108
|
+
=== Comparing against column references
|
109
|
+
|
110
|
+
You can also compare against other columns:
|
111
|
+
|
112
|
+
items.filter{|o| o.credit > :debit}.sql
|
113
|
+
#=> "SELECT * FROM items WHERE (credit > debit)
|
114
|
+
|
115
|
+
Or against SQL functions:
|
116
|
+
|
117
|
+
items.filter{|o| :price - 100 < o.max(:price)}.sql
|
118
|
+
#=> "SELECT * FROM items WHERE ((price - 100) < max(price))"
|
119
|
+
|
120
|
+
== String search functions
|
121
|
+
|
122
|
+
You can search SQL strings using the #like method:
|
123
|
+
|
124
|
+
items.filter(:name.like('Acme%')).sql
|
125
|
+
#=> "SELECT * FROM items WHERE (name LIKE 'Acme%')"
|
126
|
+
|
127
|
+
You can specify a Regexp as a like argument, but this will probably only work
|
128
|
+
on PostgreSQL and MySQL:
|
129
|
+
|
130
|
+
items.filter(:name.like(/Acme.*/)).sql
|
131
|
+
#=> "SELECT * FROM items WHERE (name ~ 'Acme.*')"
|
132
|
+
|
133
|
+
Like can also take more than one argument:
|
134
|
+
|
135
|
+
items.filter(:name.like('Acme%', /Beta.*/)).sql
|
136
|
+
#=> "SELECT * FROM items WHERE ((name LIKE 'Acme%') OR (name ~ 'Beta.*'))"
|
137
|
+
|
138
|
+
== String concatenation
|
139
|
+
|
140
|
+
You can concatenate SQL strings using Array#sql_string_join:
|
141
|
+
|
142
|
+
items.filter([:name, :comment].sql_string_join.like('%acme%')).sql
|
143
|
+
#=> "SELECT * FROM items WHERE ((name || comment) LIKE 'Acme%')"
|
144
|
+
|
145
|
+
Array#sql_string_join also takes a join argument:
|
146
|
+
|
147
|
+
items.filter([:name, :comment].sql_string_join(' ').like('%acme%')).sql
|
148
|
+
#=> "SELECT * FROM items WHERE ((name || ' ' || comment) LIKE 'Acme%')"
|
149
|
+
|
150
|
+
== Filtering using sub-queries
|
151
|
+
|
152
|
+
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:
|
153
|
+
|
154
|
+
refs = consumer_refs.filter(:logged_in).select(:consumer_id)
|
155
|
+
consumers.filter(:id => refs).sql
|
156
|
+
#=> "SELECT * FROM consumers WHERE (id IN (SELECT consumer_id FROM consumer_refs WHERE logged_in))"
|
157
|
+
|
158
|
+
Note that if you compare against a sub-query, you must select a single column in the sub-query.
|