sequel 3.35.0 → 3.36.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +78 -0
- data/Rakefile +3 -3
- data/bin/sequel +3 -1
- data/doc/advanced_associations.rdoc +154 -11
- data/doc/migration.rdoc +18 -0
- data/doc/object_model.rdoc +541 -0
- data/doc/opening_databases.rdoc +4 -1
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/schema_modification.rdoc +0 -6
- data/lib/sequel/adapters/do/mysql.rb +7 -0
- data/lib/sequel/adapters/jdbc.rb +11 -3
- data/lib/sequel/adapters/jdbc/mysql.rb +3 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +10 -8
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/mock.rb +2 -6
- data/lib/sequel/adapters/mysql.rb +3 -9
- data/lib/sequel/adapters/mysql2.rb +12 -11
- data/lib/sequel/adapters/postgres.rb +32 -40
- data/lib/sequel/adapters/shared/mssql.rb +15 -11
- data/lib/sequel/adapters/shared/mysql.rb +28 -3
- data/lib/sequel/adapters/shared/oracle.rb +5 -0
- data/lib/sequel/adapters/shared/postgres.rb +59 -5
- data/lib/sequel/adapters/shared/sqlite.rb +3 -13
- data/lib/sequel/adapters/sqlite.rb +0 -7
- data/lib/sequel/adapters/swift/mysql.rb +2 -5
- data/lib/sequel/adapters/tinytds.rb +1 -2
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
- data/lib/sequel/connection_pool/threaded.rb +9 -1
- data/lib/sequel/database/dataset_defaults.rb +3 -1
- data/lib/sequel/database/misc.rb +7 -1
- data/lib/sequel/database/query.rb +11 -3
- data/lib/sequel/database/schema_generator.rb +40 -9
- data/lib/sequel/database/schema_methods.rb +6 -1
- data/lib/sequel/dataset/actions.rb +5 -5
- data/lib/sequel/dataset/prepared_statements.rb +3 -1
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/extensions/migration.rb +28 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -9
- data/lib/sequel/extensions/pg_inet.rb +89 -0
- data/lib/sequel/extensions/pg_json.rb +178 -0
- data/lib/sequel/extensions/schema_dumper.rb +24 -6
- data/lib/sequel/model/associations.rb +19 -15
- data/lib/sequel/model/base.rb +11 -12
- data/lib/sequel/plugins/composition.rb +1 -2
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/json_serializer.rb +41 -4
- data/lib/sequel/plugins/nested_attributes.rb +72 -52
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +271 -1
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +26 -1
- data/spec/core/database_spec.rb +19 -0
- data/spec/core/dataset_spec.rb +45 -5
- data/spec/core/expression_filters_spec.rb +31 -67
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/extensions/core_extensions_spec.rb +83 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/json_serializer_spec.rb +32 -1
- data/spec/extensions/migration_spec.rb +28 -0
- data/spec/extensions/nested_attributes_spec.rb +134 -1
- data/spec/extensions/optimistic_locking_spec.rb +15 -1
- data/spec/extensions/pg_hstore_spec.rb +1 -1
- data/spec/extensions/pg_inet_spec.rb +44 -0
- data/spec/extensions/pg_json_spec.rb +101 -0
- data/spec/extensions/prepared_statements_spec.rb +30 -0
- data/spec/extensions/rcte_tree_spec.rb +9 -0
- data/spec/extensions/schema_dumper_spec.rb +195 -7
- data/spec/extensions/serialization_spec.rb +4 -0
- data/spec/extensions/spec_helper.rb +9 -1
- data/spec/extensions/tactical_eager_loading_spec.rb +8 -0
- data/spec/integration/database_test.rb +5 -1
- data/spec/integration/prepared_statement_test.rb +20 -2
- data/spec/model/associations_spec.rb +27 -0
- data/spec/model/base_spec.rb +54 -0
- data/spec/model/model_spec.rb +6 -0
- data/spec/model/record_spec.rb +18 -0
- data/spec/rcov.opts +2 -0
- metadata +14 -3
data/CHANGELOG
CHANGED
@@ -1,3 +1,81 @@
|
|
1
|
+
=== 3.36.0 (2012-06-01)
|
2
|
+
|
3
|
+
* Use Bignum generic type when dumping unsigned integer types that could potentially overflow 32-bit signed integer values (stu314)
|
4
|
+
|
5
|
+
* Support :transform option in the nested_attributes plugin, for automatically preprocessing input hashes (chanks)
|
6
|
+
|
7
|
+
* Support :unmatched_pk option in the nested_attributes plugin, can be set to :create for associated objects with natural keys (chanks)
|
8
|
+
|
9
|
+
* Support composite primary keys in the nested_attributes plugin (chanks)
|
10
|
+
|
11
|
+
* Allow Model#from_json in the json_serializer plugin to use set_fields if a :fields option is given (jeremyevans)
|
12
|
+
|
13
|
+
* Support :using option to set_column_type on PostgreSQL, to force a specific conversion from the old value to the new value (jeremyevans)
|
14
|
+
|
15
|
+
* Drop indexes in the reverse order that they were added in the schema dumper (jeremyevans)
|
16
|
+
|
17
|
+
* Add :index_names option to schema dumper method, can be set to false or :namespace (stu314, jeremyevans)
|
18
|
+
|
19
|
+
* Add Database#global_index_namespace? for checking if index namespace is global or per table (jeremyevans)
|
20
|
+
|
21
|
+
* Fix typecasting of time columns on jdbc/postgres, before could be off by a millisecond (jeremyevans)
|
22
|
+
|
23
|
+
* Add document explaining Sequel's object model (jeremyevans)
|
24
|
+
|
25
|
+
* Attempt to detect more disconnect errors in the mysql2 adapter (jeremyevans)
|
26
|
+
|
27
|
+
* Add is_current? and check_current to the migrators, for checking/raising if there are unapplied migrations (pvh, jeremyevans) (#487)
|
28
|
+
|
29
|
+
* Add a jdbc subadapter for the Progress database (Michael Gliwinski, jeremyevans)
|
30
|
+
|
31
|
+
* Add pg_inet extension, for working with PostgreSQL inet and cidr types (jeremyevans)
|
32
|
+
|
33
|
+
* Fix bug in model column setters when passing an object that raises an exception for ==('') (jeremyevans)
|
34
|
+
|
35
|
+
* Add eager_each plugin, which makes each on an eagerly loaded dataset do eager loading (jeremyevans)
|
36
|
+
|
37
|
+
* Fix bugs when parsing foreign keys for tables with explicit schema on PostgreSQL (jeremyevans)
|
38
|
+
|
39
|
+
* Remove Database#case_sensitive_like on SQLite (jeremyevans)
|
40
|
+
|
41
|
+
* Remove Database#single_value in the native sqlite adapter (jeremyevans)
|
42
|
+
|
43
|
+
* Make Dataset#get work with nil and false arguments (jeremyevans)
|
44
|
+
|
45
|
+
* Make json_serializer plugin respect :root=>:collection and :root=>:instance options (jeremyevans)
|
46
|
+
|
47
|
+
* Support savepoints in prepared transactions on MySQL 5.5.23+ (jeremyevans)
|
48
|
+
|
49
|
+
* Add pg_json extension, for working with PostgreSQL 9.2's new json type (jeremyevans)
|
50
|
+
|
51
|
+
* In the optimistic locking plugin, make refresh and save after a failed save work correctly (jeremyevans)
|
52
|
+
|
53
|
+
* Support partial indexes on Microsoft SQL Server 2008 (jeremyevans)
|
54
|
+
|
55
|
+
* Make Database#call pass blocks (jeremyevans)
|
56
|
+
|
57
|
+
* Support :each when preparing statements, useful for iterating over large datasets (jeremyevans)
|
58
|
+
|
59
|
+
* Support :if_exists and :cascade options when dropping indexes on PostgreSQL (jeremyevans)
|
60
|
+
|
61
|
+
* Support :concurrently option when adding and dropping indexes on PostgreSQL (jeremyevans)
|
62
|
+
|
63
|
+
* Make Database#transaction on PostgreSQL recognize :synchronous, :read_only, and :deferrable options (jeremyevans)
|
64
|
+
|
65
|
+
* Support :sql_mode option when connecting to MySQL (jeremyevans)
|
66
|
+
|
67
|
+
* Apply :timeout MySQL connection setting on do, jdbc, and swift adapters (jeremyevans)
|
68
|
+
|
69
|
+
* Don't set Sequel::Model.db automatically when creating an anonymous class with an associated database object (jeremyevans)
|
70
|
+
|
71
|
+
* Add :connection_handling=>:queue option to the threaded connection pools, may reduce chance of stale connections (jeremyevans) (#481)
|
72
|
+
|
73
|
+
* Handle JRuby 1.7 exception handling changes when connecting in the jdbc adapter (jeremyevans) (#477)
|
74
|
+
|
75
|
+
* Make *_to_one association setters be noops if you pass a value that is the same as the cached value (jeremyevans)
|
76
|
+
|
77
|
+
* Make Model#refresh return self when using dirty plugin (jeremyevans)
|
78
|
+
|
1
79
|
=== 3.35.0 (2012-05-01)
|
2
80
|
|
3
81
|
* Correctly handle parsing schema for tables in other databases on MySQL (jeremyevans)
|
data/Rakefile
CHANGED
@@ -128,15 +128,15 @@ begin
|
|
128
128
|
end
|
129
129
|
|
130
130
|
task :default => [:spec]
|
131
|
-
spec_with_cov.call("spec", Dir["spec/{core,model}/*_spec.rb"], "Run core and model specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/adapters/([a-ln-z]|m[a-np-z])"')}
|
131
|
+
spec_with_cov.call("spec", Dir["spec/{core,model}/*_spec.rb"], "Run core and model specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/(adapters/([a-ln-z]|m[a-np-z])|extensions/core_extensions)"')}
|
132
132
|
spec.call("spec_core", Dir["spec/core/*_spec.rb"], "Run core specs")
|
133
133
|
spec.call("spec_model", Dir["spec/model/*_spec.rb"], "Run model specs")
|
134
134
|
spec.call("_spec_model_no_assoc", Dir["spec/model/*_spec.rb"].delete_if{|f| f =~ /association|eager_loading/}, '')
|
135
|
-
spec_with_cov.call("spec_plugin", Dir["spec/extensions/*_spec.rb"], "Run extension/plugin specs")
|
135
|
+
spec_with_cov.call("spec_plugin", Dir["spec/extensions/*_spec.rb"], "Run extension/plugin specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/([a-z_]+\.rb|adapters|connection_pool|database|dataset|model)"')}
|
136
136
|
spec_with_cov.call("spec_integration", Dir["spec/integration/*_test.rb"], "Run integration tests")
|
137
137
|
|
138
138
|
%w'postgres sqlite mysql informix oracle firebird mssql db2'.each do |adapter|
|
139
|
-
spec_with_cov.call("spec_#{adapter}", ["spec/adapters/#{adapter}_spec.rb"] + Dir["spec/integration/*_test.rb"], "Run #{adapter} specs")
|
139
|
+
spec_with_cov.call("spec_#{adapter}", ["spec/adapters/#{adapter}_spec.rb"] + Dir["spec/integration/*_test.rb"], "Run #{adapter} specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/([a-z_]+\.rb|connection_pool|database|dataset|model|extensions|plugins)"')}
|
140
140
|
end
|
141
141
|
|
142
142
|
desc "Run model specs without the associations code"
|
data/bin/sequel
CHANGED
@@ -162,10 +162,12 @@ begin
|
|
162
162
|
start_time = Time.now
|
163
163
|
TO_DB = connect_proc[db2]
|
164
164
|
same_db = DB.database_type==TO_DB.database_type
|
165
|
+
index_opts = {:same_db=>same_db}
|
166
|
+
index_opts[:index_names] = :namespace if !DB.global_index_namespace? && TO_DB.global_index_namespace?
|
165
167
|
|
166
168
|
puts "Databases connections successful"
|
167
169
|
schema_migration = eval(DB.dump_schema_migration(:indexes=>false, :same_db=>same_db))
|
168
|
-
index_migration = eval(DB.dump_indexes_migration(
|
170
|
+
index_migration = eval(DB.dump_indexes_migration(index_opts))
|
169
171
|
fk_migration = eval(DB.dump_foreign_key_migration(:same_db=>same_db))
|
170
172
|
puts "Migrations dumped successfully"
|
171
173
|
|
@@ -96,25 +96,168 @@ necessary for polymorphic associations). Inside the <tt>:eager_loader</tt>
|
|
96
96
|
proc, you should get the related objects and populate the
|
97
97
|
associations cache for all objects in the array of records. The hash
|
98
98
|
of dependent associations is available for you to cascade the eager
|
99
|
-
loading down multiple levels, but it is up to you to use it.
|
100
|
-
|
99
|
+
loading down multiple levels, but it is up to you to use it.
|
100
|
+
|
101
|
+
The key_hash is a performance enhancement that is used by the default
|
101
102
|
association loaders and is also available to you. It is a hash with keys being
|
102
103
|
foreign/primary key symbols in the current table, and the values
|
103
104
|
being hashes where the key is foreign/primary key values and values
|
104
105
|
being arrays of current model objects having the foreign/primary key
|
105
106
|
value associated with the key. This is hard to visualize, so I'll
|
106
|
-
give an example
|
107
|
-
|
108
|
-
album1 = Album.load(:id=>1, :artist_id=>2)
|
109
|
-
album2 = Album.load(:id=>3, :artist_id=>2)
|
107
|
+
give an example. Let's say you have the following associations
|
108
|
+
|
110
109
|
Album.many_to_one :artist
|
111
110
|
Album.one_to_many :tracks
|
112
|
-
|
113
|
-
|
114
|
-
|
111
|
+
|
112
|
+
and the following two albums in the database:
|
113
|
+
|
114
|
+
album1 = Album.create(:artist_id=>3) # id: 1
|
115
|
+
album2 = Album.create(:artist_id=>3) # id: 2
|
116
|
+
|
117
|
+
If you try to eager load this dataset:
|
118
|
+
|
119
|
+
Album.eager(:artist, :tracks).all
|
120
|
+
|
121
|
+
Then the key_hash provided to the :eager_loader proc would be:
|
122
|
+
|
123
|
+
{:id=>{1=>[album1], 2=>[album2]}, :artist_id=>{3=>[album1, album2]}}
|
124
|
+
|
125
|
+
Let's break down the reason for the makeup of this key_hash. The hash has keys for
|
126
|
+
each of foreign/primary keys used in the association. In this case, the artist
|
127
|
+
association needs the artist_id foreign key (since it is a many_to_one), and the
|
128
|
+
tracks association needs the id primary key (since it is a one_to_many).
|
129
|
+
|
130
|
+
If you only eagerly loaded the artist association:
|
131
|
+
|
132
|
+
Album.eager(:artist).all
|
133
|
+
|
134
|
+
Then the key_hash would only contain artist_id information:
|
115
135
|
|
116
|
-
|
117
|
-
|
136
|
+
{:artist_id=>{3=>[album1, album2]}}
|
137
|
+
|
138
|
+
Likewise, if you only eagerly loaded the tracks association:
|
139
|
+
|
140
|
+
Album.eager(:tracks).all
|
141
|
+
|
142
|
+
Then the key_hash would only contain id information:
|
143
|
+
|
144
|
+
{:id=>{1=>[album1], 2=>[album2]}}
|
145
|
+
|
146
|
+
Now, the eager loader for the artist association is only going to care about the
|
147
|
+
value of the artist_id key in the hash, so it's going to do the equivalent of:
|
148
|
+
|
149
|
+
artist_id_map = key_hash[:artist_id] # {3=>[album1, album2]}
|
150
|
+
|
151
|
+
The artist_id_map contains a mapping of artist_id values to arrays of
|
152
|
+
album objects. Since it only has a single entry with a key of 3, when eagerly
|
153
|
+
loading the artists, you only need to look for artists that have an id of 3.
|
154
|
+
|
155
|
+
artists = Artist.where(:id=>artist_id_map.keys).all
|
156
|
+
|
157
|
+
When the artists are retrieved, you can iterate over them, find entries
|
158
|
+
with matching keys, and manually associate them to the albums:
|
159
|
+
|
160
|
+
artists.each do |artist|
|
161
|
+
# Find related albums using the artist_id_map
|
162
|
+
if albums = artist_id_map[artist.id]
|
163
|
+
# Iterate over the albums
|
164
|
+
albums.each do |album|
|
165
|
+
# Manually set the artist association for each album
|
166
|
+
album.associations[:artist] = artist
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
Similarly, the eager loader for the tracks association is only going to care about the
|
172
|
+
value of the id key in the hash:
|
173
|
+
|
174
|
+
id_map = key_hash[:id] # {1=>[album1], 2=>[album2]}
|
175
|
+
|
176
|
+
Now the id_map contains a mapping of id values to arrays of album objects (in this
|
177
|
+
case each array only has a single object, because id is the primary key). So when
|
178
|
+
looking for tracks to eagerly load, you only need to look for ones that have an
|
179
|
+
album_id of 1 or 2:
|
180
|
+
|
181
|
+
tracks = Track.where(:album_id=>id_map.keys).all
|
182
|
+
|
183
|
+
When the tracks are retrieved, you can iterate over them, find entries with matching
|
184
|
+
keys, and manually associate them to the albums:
|
185
|
+
|
186
|
+
tracks.each do |track|
|
187
|
+
if albums = id_map[track.album_id]
|
188
|
+
albums.each do |album|
|
189
|
+
album.associations[:tracks] << track
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
=== Two basic example eager loaders
|
195
|
+
|
196
|
+
Putting the code in the above examples together, you almost have enough for a basic
|
197
|
+
working eager loader. The main important thing that is missing is you need to set
|
198
|
+
initial values for the eagerly loaded associations. For the artist association, you
|
199
|
+
need to initial the values to nil:
|
200
|
+
|
201
|
+
# rows here is the :rows entry in the hash passed to the eager loader
|
202
|
+
rows.each{|album| album.associations[:artist] = nil}
|
203
|
+
|
204
|
+
For the tracks association, you set the initial value to an empty array:
|
205
|
+
|
206
|
+
rows.each{|album| album.associations[:track] = []}
|
207
|
+
|
208
|
+
These are done so that if an album currently being loaded doesn't have an associated
|
209
|
+
artist or any associated tracks, the lack of them will be cached, so calling the
|
210
|
+
artist or tracks method on the album will not do another database lookup.
|
211
|
+
|
212
|
+
So putting everything together, the artist eager loader looks like:
|
213
|
+
|
214
|
+
:eager_loader=>(proc do |eo_opts|
|
215
|
+
eo_opts[:rows].each{|album| album.associations[:artist] = nil}
|
216
|
+
artist_id_map = eo_opts[:key_hash][:artist_id]
|
217
|
+
Artist.where(:id=>artist_id_map.keys).all do |artist|
|
218
|
+
if albums = artist_id_map[artist.id]
|
219
|
+
albums.each do |album|
|
220
|
+
album.associations[:artist] = artist
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end)
|
225
|
+
|
226
|
+
and the tracks eager loader looks like:
|
227
|
+
|
228
|
+
:eager_loader=>(proc do |eo_opts|
|
229
|
+
eo_opts[:rows].each{|album| album.associations[:tracks] = []}
|
230
|
+
id_map = eo_opts[:key_hash][:id]
|
231
|
+
Track.where(:id=>id_map.keys).all do |tracks|
|
232
|
+
if albums = id_map[track.album_id]
|
233
|
+
albums.each do |album|
|
234
|
+
album.associations[:tracks] << track
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end)
|
239
|
+
|
240
|
+
Now, these are both overly simplistic eager loaders that don't respect cascaded
|
241
|
+
associations or any of the association options. But hopefully they both
|
242
|
+
provide simple examples that you can more easily build and learn from, as
|
243
|
+
the custom eager loaders described later in this page are more complex.
|
244
|
+
|
245
|
+
Basically, the eager loading steps can be broken down into:
|
246
|
+
|
247
|
+
1. Set default association values (nil/[]) for each of the current objects
|
248
|
+
2. Build a custom key map mapping foreign/primary key values to arrays of
|
249
|
+
current objects (using the key_hash)
|
250
|
+
3. Return just related associated objects by filtering the associated class
|
251
|
+
to include only matching values
|
252
|
+
4. Iterating over the returned associated objects, indexing into the custom key
|
253
|
+
map using the foreign/primary key value in the associated object to get
|
254
|
+
current values associated to that specific object.
|
255
|
+
5. For each of those current values, updating the cached association value to
|
256
|
+
include that specific object.
|
257
|
+
|
258
|
+
Using the :eager_loader proc, you should be able to eagerly load all associations
|
259
|
+
that can be eagerly loaded, even if Sequel doesn't natively support such eager
|
260
|
+
loading.
|
118
261
|
|
119
262
|
== ActiveRecord associations
|
120
263
|
|
data/doc/migration.rdoc
CHANGED
@@ -534,6 +534,24 @@ with an empty database, and attempt to recreate the schema using:
|
|
534
534
|
|
535
535
|
sequel -m db/migrations postgres://host/database
|
536
536
|
|
537
|
+
== Checking for Current Migrations
|
538
|
+
|
539
|
+
In your application code, you may want to check that you are up to date in
|
540
|
+
regards to migrations (i.e. you don't have any unapplied migrations). Sequel
|
541
|
+
offers two separate methods to do that. The first is Sequel::Migrator.check_current.
|
542
|
+
This method raises an exception if there are outstanding migrations that need to
|
543
|
+
be run. The second is Sequel::Migrator.is_current?, which returns true if there
|
544
|
+
are no outstanding migrations, and false if there are outstanding migrations.
|
545
|
+
|
546
|
+
If you want to ensure that your application code is up to date, you may want to
|
547
|
+
add the following code after connecting to your database:
|
548
|
+
|
549
|
+
Sequel.extension :migration
|
550
|
+
Sequel::Migrator.check_current(DB, '/path/to/migrations')
|
551
|
+
|
552
|
+
This will cause your application to raise an error when you start it if you have
|
553
|
+
any outstanding migrations.
|
554
|
+
|
537
555
|
== Old-style migration classes
|
538
556
|
|
539
557
|
Before the <tt>Sequel.migration</tt> DSL was introduced, Sequel used classes
|
@@ -0,0 +1,541 @@
|
|
1
|
+
= The Sequel Object Model
|
2
|
+
|
3
|
+
Sequel's dataset layer is mostly structured as an DSL, so it often obscures
|
4
|
+
what actual objects are being used. For example, you don't usually create
|
5
|
+
Sequel objects by calling #new on the object's class (other than Sequel::Model
|
6
|
+
subclasses). However, just as almost everything in ruby is an object, all
|
7
|
+
the methods you call in Sequel deal with objects behind the scenes.
|
8
|
+
|
9
|
+
There are five main types of Sequel-specific objects that you deal with in
|
10
|
+
Sequel:
|
11
|
+
|
12
|
+
* Sequel::Database
|
13
|
+
* Sequel::Dataset
|
14
|
+
* Sequel::Model
|
15
|
+
* Standard Ruby Types
|
16
|
+
* Sequel::SQL::Expression (and subclasses)
|
17
|
+
|
18
|
+
== Sequel::Database
|
19
|
+
|
20
|
+
Sequel::Database is the main Sequel object that you deal with. It's usually
|
21
|
+
created by the Sequel.connect method:
|
22
|
+
|
23
|
+
DB = Sequel.connect('postgres://host/database')
|
24
|
+
|
25
|
+
A Sequel::Database object represents the database you are connecting to.
|
26
|
+
Sequel::Database handles things like Sequel::Dataset creation,
|
27
|
+
|
28
|
+
dataset = DB[:table]
|
29
|
+
|
30
|
+
schema modification,
|
31
|
+
|
32
|
+
DB.create_table(:table) do
|
33
|
+
primary_key :id
|
34
|
+
String :name
|
35
|
+
end
|
36
|
+
|
37
|
+
and transactions:
|
38
|
+
|
39
|
+
DB.transaction do
|
40
|
+
DB[:table].insert(:column=>value)
|
41
|
+
end
|
42
|
+
|
43
|
+
Sequel::Database#literal can be used to take any object that Sequel handles
|
44
|
+
and literalize the object to an SQL string fragment:
|
45
|
+
|
46
|
+
DB.literal(DB[:table]) # (SELECT * FROM "table")
|
47
|
+
|
48
|
+
== Sequel::Dataset
|
49
|
+
|
50
|
+
Sequel::Dataset objects represent SQL queries, or more generally, they represent
|
51
|
+
abstract collections of rows in the database. They are usually created from
|
52
|
+
a Sequel::Database object:
|
53
|
+
|
54
|
+
dataset = DB[:table] # SELECT * FROM "table"
|
55
|
+
dataset = DB.from(table) # SELECT * FROM "table"
|
56
|
+
dataset = DB.select(:column) # SELECT "column"
|
57
|
+
|
58
|
+
Most Sequel::Dataset methods return modified copies of the receiver, and the
|
59
|
+
general way to build queries in Sequel is via a method chain:
|
60
|
+
|
61
|
+
dataset = DB[:test].
|
62
|
+
select(:column1, :column2).
|
63
|
+
where(:column3 => 4).
|
64
|
+
order(:column5)
|
65
|
+
|
66
|
+
Such a method chain is a more direct way of doing:
|
67
|
+
|
68
|
+
dataset = DB[:test]
|
69
|
+
dataset = dataset.select(:column1, :column2)
|
70
|
+
dataset = dataset.where(:column3 => 4)
|
71
|
+
dataset = dataset.order(:column5)
|
72
|
+
|
73
|
+
When you are ready to execute your query, you call one of the Sequel::Dataset
|
74
|
+
action methods. For returning rows, you can do:
|
75
|
+
|
76
|
+
dataset.first
|
77
|
+
dataset.all
|
78
|
+
dataset.each{|row| row}
|
79
|
+
|
80
|
+
For inserting, updating, or deleting rows, you can do:
|
81
|
+
|
82
|
+
dataset.insert(:column=>value)
|
83
|
+
dataset.update(:column=>value)
|
84
|
+
dataset.delete
|
85
|
+
|
86
|
+
All datasets are related to their database object, which you can access via
|
87
|
+
the Sequel::Dataset#db method:
|
88
|
+
|
89
|
+
dataset.db # => DB
|
90
|
+
|
91
|
+
== Sequel::Model
|
92
|
+
|
93
|
+
Sequel::Model objects are wrappers around a particular Sequel::Dataset object that
|
94
|
+
add custom behavior, both custom behavior for the entire set of rows in the dataset
|
95
|
+
(the model's class methods), custom behavior for a subset of rows in the dataset
|
96
|
+
(the model's dataset methods), and custom behavior for single rows in the dataset
|
97
|
+
(the model's instance methods).
|
98
|
+
|
99
|
+
Unlike most other Sequel objects, Sequel::Model classes and instances are
|
100
|
+
generally created by the user using standard ruby syntax:
|
101
|
+
|
102
|
+
class Album < Sequel::Model
|
103
|
+
end
|
104
|
+
album = Album.new
|
105
|
+
|
106
|
+
All model classes are related to their Sequel::Dataset object, which you
|
107
|
+
can access via the Sequel::Model.dataset method:
|
108
|
+
|
109
|
+
Album.dataset # SELECT * FROM "albums"
|
110
|
+
|
111
|
+
Additionally, all model classes are related to their dataset's Sequel::Database
|
112
|
+
object, which you can access via the Sequel::Model.db method:
|
113
|
+
|
114
|
+
Album.db # => DB
|
115
|
+
|
116
|
+
== Standard Ruby Types
|
117
|
+
|
118
|
+
Where possible, Sequel uses ruby's standard types to represent SQL concepts.
|
119
|
+
In the examples here, the text to the right side of the # sign is the output
|
120
|
+
if you pass the left side to Sequel::Database#literal.
|
121
|
+
|
122
|
+
=== Symbol
|
123
|
+
|
124
|
+
For example, ruby symbols represent SQL identifiers (tables, columns, schemas):
|
125
|
+
|
126
|
+
:table # "table"
|
127
|
+
:column # "column"
|
128
|
+
|
129
|
+
However, they can also represent qualified identifiers by including a double
|
130
|
+
underscore inside a symbol:
|
131
|
+
|
132
|
+
:table__column # "table"."column"
|
133
|
+
|
134
|
+
They can also represent an aliased identifier by including a triple underscore
|
135
|
+
inside a symbol:
|
136
|
+
|
137
|
+
:column___alias # "column" AS "alias"
|
138
|
+
|
139
|
+
You can combine both qualification and aliasing by using a double underscore
|
140
|
+
and a triple underscore:
|
141
|
+
|
142
|
+
:table__column___alias # "table"."column" AS "alias"
|
143
|
+
|
144
|
+
=== Integer, Float, BigDecimal, String, Date, Time, DateTime
|
145
|
+
|
146
|
+
Ruby's Integer, Float, BigDecimal, String, Date, Time, and DateTime classes
|
147
|
+
represent similar types in SQL:
|
148
|
+
|
149
|
+
1 # 1
|
150
|
+
1.0 # 1.0
|
151
|
+
BigDecimal.new('1.0') # 1.0
|
152
|
+
"string" # 'string'
|
153
|
+
Date.new(2012, 5, 6) # '2012-05-06'
|
154
|
+
Time.now # '2012-05-06 10:20:30'
|
155
|
+
DateTime.now # '2012-05-06 10:20:30'
|
156
|
+
|
157
|
+
=== Hash
|
158
|
+
|
159
|
+
Sequel generally uses hash objects to represent equality:
|
160
|
+
|
161
|
+
{:column => 1} # ("column" = 1)
|
162
|
+
|
163
|
+
However, if you use in array as the hash value, it will usually be used to represent inclusion:
|
164
|
+
|
165
|
+
{:column => [1, 2, 3]} # ("column" IN (1, 2, 3))
|
166
|
+
|
167
|
+
You can also use a Sequel::Dataset instance as the hash value, which will be used to
|
168
|
+
represent inclusion in the subselect:
|
169
|
+
|
170
|
+
{:column => DB[:table].select(:column)} # ("column" IN (SELECT "column" FROM "table"))
|
171
|
+
|
172
|
+
If you pass true, false, or nil as the hash value, it will be used to represent identity:
|
173
|
+
|
174
|
+
{:column => nil} # ("column" IS NULL)
|
175
|
+
|
176
|
+
If you pass a Range object, it will be used as the bounds for a greater than and less than
|
177
|
+
operation:
|
178
|
+
|
179
|
+
{:column => 1..2} # (("column" >= 1) AND ("column" <= 2))
|
180
|
+
{:column => 1...3} # (("column" >= 1) AND ("column" < 3))
|
181
|
+
|
182
|
+
If you pass a Regexp object as the value, it will be used as a regular expression
|
183
|
+
operation (only supported on PostgreSQL and MySQL currently):
|
184
|
+
|
185
|
+
{:column => /a.*b/} # ("column" ~ 'a.*b')
|
186
|
+
|
187
|
+
=== Array
|
188
|
+
|
189
|
+
Sequel generally treats arrays as an SQL value list:
|
190
|
+
|
191
|
+
[1, 2, 3] # (1, 2, 3)
|
192
|
+
|
193
|
+
However, if all members of the array are arrays with two members, then the array is treated like
|
194
|
+
a hash:
|
195
|
+
|
196
|
+
[[:column, 1]] # ("column" = 1)
|
197
|
+
|
198
|
+
The advantage of using an array over a hash for such a case is that a hash cannot include
|
199
|
+
multiple objects with the same key, while the array can.
|
200
|
+
|
201
|
+
== Sequel::SQL::Expression (and subclasses)
|
202
|
+
|
203
|
+
If Sequel needs to represent an SQL concept that does not map directly to an existing
|
204
|
+
ruby class, it will generally use a Sequel::SQL::Expression subclass to represent that
|
205
|
+
concept.
|
206
|
+
|
207
|
+
=== Sequel::LiteralString
|
208
|
+
|
209
|
+
Sequel::LiteralString is not actually a Sequel::SQL::Expression subclass. It is
|
210
|
+
a subclass of String, but it is treated specially by Sequel, in that it is treated
|
211
|
+
as literal SQL code, instead of as an SQL string that needs to be escaped:
|
212
|
+
|
213
|
+
Sequel::LiteralString.new("co'de") # co'de
|
214
|
+
|
215
|
+
The following shortcuts exist for creating Sequel::LiteralString objects:
|
216
|
+
|
217
|
+
Sequel.lit("co'de")
|
218
|
+
"co'de".lit
|
219
|
+
|
220
|
+
=== Sequel::SQL::Blob
|
221
|
+
|
222
|
+
Sequel::SQL::Blob is also a String subclass, but it is treated as an SQL blob
|
223
|
+
instead of an SQL string, as SQL blobs often have different literalization rules
|
224
|
+
than SQL strings do:
|
225
|
+
|
226
|
+
Sequel::SQL::Blob.new("blob")
|
227
|
+
|
228
|
+
The following shortcuts exist for creating Sequel::SQL::Blob objects:
|
229
|
+
|
230
|
+
Sequel.blob("blob")
|
231
|
+
"blob".to_sequel_blob
|
232
|
+
|
233
|
+
=== Sequel::SQLTime
|
234
|
+
|
235
|
+
Sequel::SQLTime is a Time subclass. However, it is treated specially by Sequel
|
236
|
+
in that only the time component is literalized, not the date part. This type
|
237
|
+
is used to represent SQL time types, which do not contain date information.
|
238
|
+
|
239
|
+
Sequel::SQLTime.create(10, 20, 30) # "10:20:30"
|
240
|
+
|
241
|
+
=== Sequel::SQL::ValueList
|
242
|
+
|
243
|
+
Sequel::SQL::ValueList objects always represent SQL value lists. Most ruby arrays
|
244
|
+
represent value lists in SQL, except that arrays of two-element arrays are treated
|
245
|
+
similar to hashes. Such arrays can be wrapped in this class to ensure they are
|
246
|
+
treated as value lists. This is important when doing a composite key IN lookup,
|
247
|
+
which some databases support. Sequel::SQL::ValueList is an ::Array subclass with
|
248
|
+
no additional behavior, so it can be instantiated like a normal array:
|
249
|
+
|
250
|
+
Sequel::SQL::ValueList.new([[1, 2], [3, 4]]) # ((1, 2), (3, 4))
|
251
|
+
|
252
|
+
In old versions of Sequel, these objects often needed to be created manually,
|
253
|
+
but in newer versions of Sequel, they are created automatically in most cases
|
254
|
+
where they are required.
|
255
|
+
|
256
|
+
The following shortcuts exist for creating Sequel::SQL::ValueList objects:
|
257
|
+
|
258
|
+
Sequel.value_list([[1, 2], [3, 4]])
|
259
|
+
[[1, 2], [3, 4]].sql_value_list
|
260
|
+
|
261
|
+
=== Sequel::SQL::Identifier
|
262
|
+
|
263
|
+
Sequel::SQL::Identifier objects represent single identifiers. The main reason for
|
264
|
+
their existance is that they are not checked for double or triple underscores, so no
|
265
|
+
automatic qualification or aliasing happens for them:
|
266
|
+
|
267
|
+
Sequel::SQL::Identifier.new(:col__umn) # "col__umn"
|
268
|
+
|
269
|
+
The following shortcuts exist for creating Sequel::SQL::Identifier objects:
|
270
|
+
|
271
|
+
Sequel.identifier(:col__umn)
|
272
|
+
:col__umn.identifier
|
273
|
+
|
274
|
+
=== Sequel::SQL::QualifiedIdentifier
|
275
|
+
|
276
|
+
Sequel::SQL::QualifiedIdentifier objects represent qualified identifiers:
|
277
|
+
|
278
|
+
Sequel::SQL::QualifiedIdentifier.new(:table, :column) # "table"."column"
|
279
|
+
|
280
|
+
The following shortcuts exist for creating Sequel::SQL::QualifiedIdentifier objects:
|
281
|
+
|
282
|
+
Sequel.qualify(:table, :column)
|
283
|
+
:column.qualify(:table)
|
284
|
+
|
285
|
+
=== Sequel::SQL::AliasedExpression
|
286
|
+
|
287
|
+
Sequel::SQL::AliasedExpression objects represent aliased expressions in SQL. The alias
|
288
|
+
is treated as an identifier, but the expression can be an arbitrary Sequel expression:
|
289
|
+
|
290
|
+
Sequel::SQL::AliasedExpression.new(:column, :alias) # "column" AS "alias"
|
291
|
+
|
292
|
+
The following shortcuts exist for creating Sequel::SQL::AliasedExpression objects:
|
293
|
+
|
294
|
+
Sequel.as(:column, :alias)
|
295
|
+
:column.as(:alias)
|
296
|
+
|
297
|
+
=== Sequel::SQL::ComplexExpression
|
298
|
+
|
299
|
+
Sequel::SQL::ComplexExpression objects mostly represent SQL operations with arguments.
|
300
|
+
There are separate subclasses for representing boolean operations such as AND and OR
|
301
|
+
(Sequel::SQL::BooleanExpression), mathematical operations such as + and -
|
302
|
+
(Sequel::SQL::NumericExpression), and string operations such as || and LIKE
|
303
|
+
(Sequel::SQL::StringExpression).
|
304
|
+
|
305
|
+
Sequel::SQL::BooleanExpression.new(:OR, :col1, :col2) # ("col1" OR "col2")
|
306
|
+
Sequel::SQL::NumericExpression.new(:+, :column, 2) # ("column" + 2)
|
307
|
+
Sequel::SQL::StringExpression.new(:"||", :column, "b") # ("column" || 'b')
|
308
|
+
|
309
|
+
There are many shortcuts for creating Sequel::SQL::ComplexExpression objects:
|
310
|
+
|
311
|
+
Sequel.or(:col1, :col2)
|
312
|
+
:col1 | :col2
|
313
|
+
|
314
|
+
Sequel.+(:column, 2)
|
315
|
+
:column + 2
|
316
|
+
|
317
|
+
Sequel.join([:column, 'b'])
|
318
|
+
:column + 'b'
|
319
|
+
|
320
|
+
=== Sequel::SQL::CaseExpression
|
321
|
+
|
322
|
+
Sequel::SQL::CaseExpression objects represent SQL CASE expressions, which represent
|
323
|
+
branches in the database, similar to ruby case expressions. Like ruby's case
|
324
|
+
expressions, these case expressions can have a implicit value you are comparing
|
325
|
+
against:
|
326
|
+
|
327
|
+
Sequel::SQL::CaseExpression.new({2=>1}, 0, :a) # CASE "a" WHEN 2 THEN 1 ELSE 0 END
|
328
|
+
|
329
|
+
Or they can treat each condition separately:
|
330
|
+
|
331
|
+
Sequel::SQL::CaseExpression.new({{:a=>2}=>1}, 0) # CASE WHEN ("a" = 2) THEN 1 ELSE 0 END
|
332
|
+
|
333
|
+
In addition to providing a hash, you can also provide an array of two-element arrays:
|
334
|
+
|
335
|
+
Sequel::SQL::CaseExpression.new([[2, 1]], 0, :a) # CASE "a" WHEN 2 THEN 1 ELSE 0 END
|
336
|
+
|
337
|
+
The following shortcuts exist for creating Sequel::SQL::CaseExpression objects:
|
338
|
+
|
339
|
+
Sequel.case({2=>1}, 0, :a)
|
340
|
+
Sequel.case({{:a=>2}=>1}, 0)
|
341
|
+
|
342
|
+
{2=>1}.case(0, :a)
|
343
|
+
{{:a=>2}=>1}.case(0)
|
344
|
+
|
345
|
+
=== Sequel::SQL::Cast
|
346
|
+
|
347
|
+
Sequel::SQL::Cast objects represent CAST expressions in SQL, which does explicit
|
348
|
+
typecasting in the database. With Sequel, you provide the expression to typecast
|
349
|
+
as well as the type to cast to. The type can either be a generic type, given as
|
350
|
+
a ruby class:
|
351
|
+
|
352
|
+
Sequel::SQL::Cast.new(:a, String) # (CAST "a" AS text)
|
353
|
+
|
354
|
+
or a specific type, given as a symbol or string:
|
355
|
+
|
356
|
+
Sequel::SQL::Cast.new(:a, :int4) # (CAST "a" AS int4)
|
357
|
+
|
358
|
+
The following shortcuts exist for creating Sequel::SQL::Cast objects:
|
359
|
+
|
360
|
+
Sequel.cast(:a, String)
|
361
|
+
Sequel.cast(:a, :int4)
|
362
|
+
|
363
|
+
:a.cast(String)
|
364
|
+
:a.cast(:int4)
|
365
|
+
|
366
|
+
=== Sequel::SQL::ColumnAll
|
367
|
+
|
368
|
+
Sequel::SQL::ColumnAll objects represent the selection of all columns from a
|
369
|
+
table. They are pretty much only used as arguments to one of the Dataset select
|
370
|
+
methods, and are not used much anymore since Dataset#select_all was expanded to
|
371
|
+
take arguments. Still, it's possible they are still useful in some code:
|
372
|
+
|
373
|
+
Sequel::SQL::ColumnAll.new(:table) # "table".*
|
374
|
+
|
375
|
+
The following shortcut exists for creating Sequel::SQL::Cast objects:
|
376
|
+
|
377
|
+
:table.*
|
378
|
+
|
379
|
+
=== Sequel::SQL::Constant
|
380
|
+
|
381
|
+
Sequel::SQL::Constant objects represent constants or psuedo-constants in SQL,
|
382
|
+
such as TRUE, NULL, and CURRENT_TIMESTAMP. These are not designed to be created
|
383
|
+
or used by the end user, but some existing values are predefined under the
|
384
|
+
Sequel namespace:
|
385
|
+
|
386
|
+
Sequel::CURRENT_TIMESTAMP # CURRENT_TIMESTAMP
|
387
|
+
|
388
|
+
These objects are usually used as values in queries:
|
389
|
+
|
390
|
+
DB[:table].insert(:time=>Sequel::CURRENT_TIMESTAMP)
|
391
|
+
|
392
|
+
=== Sequel::SQL::Function
|
393
|
+
|
394
|
+
Sequel::SQL::Function objects represents database function calls, which take a function
|
395
|
+
name and any arguments:
|
396
|
+
|
397
|
+
Sequel::SQL::Function.new(:func, :a, 2) # func("a", 2)
|
398
|
+
|
399
|
+
The following shortcuts exist for creating Sequel::SQL::Function objects:
|
400
|
+
|
401
|
+
Sequel.function(:func, :a, 2)
|
402
|
+
:func.sql_function(:a, 2)
|
403
|
+
|
404
|
+
=== Sequel::SQL::JoinClause
|
405
|
+
|
406
|
+
Sequel::SQL::JoinClause objects represent SQL JOIN clauses. They are usually
|
407
|
+
not created manually, as the Dataset join methods create them automatically.
|
408
|
+
|
409
|
+
=== Sequel::SQL::PlaceholderLiteralString
|
410
|
+
|
411
|
+
Sequel::SQL::PlaceholderLiteralString objects represent a literal SQL string
|
412
|
+
with placeholders for variables. There are three types of these objects.
|
413
|
+
The first type uses question marks with multiple placeholder value objects:
|
414
|
+
|
415
|
+
Sequel::SQL::PlaceholderLiteralString.new('? = ?', [:a, 1]) # "a" = 1
|
416
|
+
|
417
|
+
The second uses named placeholders with colons and a hash of placeholder
|
418
|
+
value objects:
|
419
|
+
|
420
|
+
Sequel::SQL::PlaceholderLiteralString.new(':b = :v', [{:b=>:a, :v=>1}]) # "a" = 1
|
421
|
+
|
422
|
+
The third uses an array instead of a string, with multiple placeholder
|
423
|
+
objects, each one going in between the members of the array:
|
424
|
+
|
425
|
+
Sequel::SQL::PlaceholderLiteralString.new(['', ' = '], [:a, 1]) # "a" = 1
|
426
|
+
|
427
|
+
For any of these three forms, you can also include a third argument for whether
|
428
|
+
to include parentheses around the string:
|
429
|
+
|
430
|
+
Sequel::SQL::PlaceholderLiteralString.new('? = ?', [:a, 1], true) # ("a" = 1)
|
431
|
+
|
432
|
+
The following shortcuts exist for creating Sequel::SQL::PlaceholderLiteralString
|
433
|
+
objects:
|
434
|
+
|
435
|
+
Sequel.lit('? = ?', :a, 1)
|
436
|
+
Sequel.lit(':b = :v', :b=>:a, :v=>1)
|
437
|
+
Sequel.lit(['', ' = '], :a, 1)
|
438
|
+
|
439
|
+
'? = ?'.lit(:a, 1)
|
440
|
+
':b = :v'.lit(:b=>:a, :v=>1)
|
441
|
+
|
442
|
+
=== Sequel::SQL::OrderedExpression
|
443
|
+
|
444
|
+
Sequel::SQL::OrderedExpression objects represent ascending or descending sorts,
|
445
|
+
used by the Dataset order methods. They take an expression, and whether to sort
|
446
|
+
it ascending or descending:
|
447
|
+
|
448
|
+
Sequel::SQL::OrderedExpression.new(:a) # "a" DESC
|
449
|
+
Sequel::SQL::OrderedExpression.new(:a, false) # "a" ASC
|
450
|
+
|
451
|
+
Additionally, they take an options hash, which can be used to specify how nulls
|
452
|
+
can be sorted:
|
453
|
+
|
454
|
+
Sequel::SQL::OrderedExpression.new(:a, true, :nulls=>:first) # "a" DESC NULLS FIRST
|
455
|
+
Sequel::SQL::OrderedExpression.new(:a, false, :nulls=>:last) # "a" ASC NULLS LAST
|
456
|
+
|
457
|
+
The following shortcuts exist for creating Sequel::SQL::OrderedExpression objects:
|
458
|
+
|
459
|
+
Sequel.asc(:a)
|
460
|
+
Sequel.desc(:a)
|
461
|
+
Sequel.asc(:a, :nulls=>:first)
|
462
|
+
Sequel.desc(:a, :nulls=>:last)
|
463
|
+
|
464
|
+
:a.asc
|
465
|
+
:a.desc
|
466
|
+
:a.asc(:nulls=>:first)
|
467
|
+
:a.desc(:nulls=>:last)
|
468
|
+
|
469
|
+
=== Sequel::SQL::Subscript
|
470
|
+
|
471
|
+
Sequel::SQL::Subscript objects represent SQL database array access. They take an
|
472
|
+
expression and an array of indexes:
|
473
|
+
|
474
|
+
Sequel::SQL::Subscript.new(:a, [1]) # "a"[1]
|
475
|
+
Sequel::SQL::Subscript.new(:a, [1, 2]) # "a"[1, 2]
|
476
|
+
|
477
|
+
The following shortcuts exist for creating Sequel::SQL::Subscript objects:
|
478
|
+
|
479
|
+
Sequel.subscript(:a, 1)
|
480
|
+
Sequel.subscript(:a, 1, 2)
|
481
|
+
|
482
|
+
:a.sql_subscript(1)
|
483
|
+
:a.sql_subscript(1, 2)
|
484
|
+
|
485
|
+
=== Sequel::SQL::VirtualRow
|
486
|
+
|
487
|
+
Sequel::SQL::VirtualRow is a BasicObject subclass that is the backbone behind the
|
488
|
+
block expression support:
|
489
|
+
|
490
|
+
DB[:table].filter{a < 1}
|
491
|
+
|
492
|
+
In the above code, the block is instance-evaled instead a VirtualRow instance.
|
493
|
+
|
494
|
+
These objects are usually not instantiated manually. See the
|
495
|
+
{Virtual Row Guide}[link:files/doc/virtual_rows_rdoc.html] for details.
|
496
|
+
|
497
|
+
=== Sequel::SQL::Window
|
498
|
+
|
499
|
+
Sequel::SQL::Window objects represent the windows used by Sequel::SQL::WindowFunction.
|
500
|
+
They use a hash-based API, supporting the :frame, :order, :partition, and :window
|
501
|
+
options:
|
502
|
+
|
503
|
+
Sequel::SQL::Window.new(:order=>:a) # (ORDER BY "a")
|
504
|
+
Sequel::SQL::Window.new(:parition=>:a) # (PARTITION BY "a")
|
505
|
+
|
506
|
+
Sequel::SQL::Window.new(:parition=>:a, :frame=>:all)
|
507
|
+
# (PARTITION BY "a" ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
|
508
|
+
|
509
|
+
=== Sequel::SQL::WindowFunction
|
510
|
+
|
511
|
+
Sequel::SQL::WindowFunction objects represent SQL window function calls. These
|
512
|
+
just combine a Sequel::SQL::Function with a Sequel::SQL::Window:
|
513
|
+
|
514
|
+
function = Sequel::SQL::Function.new(:f, 1)
|
515
|
+
window = Sequel::SQL::Window.new(:order=>:a)
|
516
|
+
Sequel::SQL::WindowFunction.new(function, window) # f(1) OVER (ORDER BY "a")
|
517
|
+
|
518
|
+
Virtual rows offer a shortcut for creating Sequel::SQL::Window objects.
|
519
|
+
|
520
|
+
=== Sequel::SQL::Wrapper
|
521
|
+
|
522
|
+
Sequel::SQL::Wrapper objects wrap arbitrary objects so that they can be used
|
523
|
+
in Sequel expressions:
|
524
|
+
|
525
|
+
o = Object.new
|
526
|
+
def o.sql_literal(ds) "foo" end
|
527
|
+
Sequel::SQL::Wrapper.new(o) # foo
|
528
|
+
|
529
|
+
The advantage of wrapping the object is that you can the call Sequel methods
|
530
|
+
on the wrapper that would not be defined on the object itself:
|
531
|
+
|
532
|
+
Sequel::SQL::Wrapper.new(o) + 1 # (foo + 1)
|
533
|
+
|
534
|
+
You can use the Sequel.expr method to wrap any object:
|
535
|
+
|
536
|
+
Sequel.expr(o)
|
537
|
+
|
538
|
+
However, note that that does not necessarily return a Sequel::SQL::Wrapper
|
539
|
+
object, it may return a different class of object, such as a
|
540
|
+
Sequel::SQL::ComplexExpression subclass object.
|
541
|
+
|