sequel 3.35.0 → 3.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +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
|
+
|