sequel 3.13.0 → 3.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/CHANGELOG +36 -0
  2. data/doc/release_notes/3.14.0.txt +118 -0
  3. data/lib/sequel/adapters/oracle.rb +7 -2
  4. data/lib/sequel/adapters/shared/mssql.rb +9 -3
  5. data/lib/sequel/connection_pool/sharded_threaded.rb +1 -1
  6. data/lib/sequel/connection_pool/threaded.rb +3 -3
  7. data/lib/sequel/database/connecting.rb +47 -11
  8. data/lib/sequel/database/dataset.rb +17 -6
  9. data/lib/sequel/database/dataset_defaults.rb +15 -3
  10. data/lib/sequel/database/logging.rb +4 -3
  11. data/lib/sequel/database/misc.rb +33 -21
  12. data/lib/sequel/database/query.rb +61 -22
  13. data/lib/sequel/database/schema_generator.rb +108 -45
  14. data/lib/sequel/database/schema_methods.rb +8 -5
  15. data/lib/sequel/dataset/actions.rb +194 -45
  16. data/lib/sequel/dataset/features.rb +1 -1
  17. data/lib/sequel/dataset/graph.rb +51 -43
  18. data/lib/sequel/dataset/misc.rb +29 -5
  19. data/lib/sequel/dataset/mutation.rb +0 -1
  20. data/lib/sequel/dataset/prepared_statements.rb +14 -2
  21. data/lib/sequel/dataset/query.rb +268 -125
  22. data/lib/sequel/dataset/sql.rb +33 -44
  23. data/lib/sequel/extensions/migration.rb +3 -2
  24. data/lib/sequel/extensions/pagination.rb +1 -1
  25. data/lib/sequel/model/associations.rb +89 -87
  26. data/lib/sequel/model/base.rb +386 -109
  27. data/lib/sequel/model/errors.rb +15 -1
  28. data/lib/sequel/model/exceptions.rb +3 -3
  29. data/lib/sequel/model/inflections.rb +2 -2
  30. data/lib/sequel/model/plugins.rb +9 -5
  31. data/lib/sequel/plugins/rcte_tree.rb +43 -15
  32. data/lib/sequel/plugins/schema.rb +6 -5
  33. data/lib/sequel/plugins/serialization.rb +1 -1
  34. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  35. data/lib/sequel/plugins/tree.rb +33 -1
  36. data/lib/sequel/timezones.rb +16 -10
  37. data/lib/sequel/version.rb +1 -1
  38. data/spec/adapters/mssql_spec.rb +36 -2
  39. data/spec/adapters/mysql_spec.rb +4 -4
  40. data/spec/adapters/postgres_spec.rb +1 -1
  41. data/spec/adapters/spec_helper.rb +2 -2
  42. data/spec/core/database_spec.rb +8 -1
  43. data/spec/core/dataset_spec.rb +36 -1
  44. data/spec/extensions/pagination_spec.rb +1 -1
  45. data/spec/extensions/rcte_tree_spec.rb +40 -8
  46. data/spec/extensions/schema_spec.rb +5 -0
  47. data/spec/extensions/serialization_spec.rb +4 -4
  48. data/spec/extensions/single_table_inheritance_spec.rb +7 -0
  49. data/spec/extensions/tree_spec.rb +36 -0
  50. data/spec/integration/dataset_test.rb +19 -0
  51. data/spec/integration/prepared_statement_test.rb +2 -2
  52. data/spec/integration/schema_test.rb +1 -1
  53. data/spec/integration/spec_helper.rb +4 -4
  54. data/spec/integration/timezone_test.rb +27 -21
  55. data/spec/model/associations_spec.rb +5 -5
  56. data/spec/model/dataset_methods_spec.rb +13 -0
  57. data/spec/model/hooks_spec.rb +31 -0
  58. data/spec/model/record_spec.rb +24 -7
  59. data/spec/model/validations_spec.rb +9 -4
  60. metadata +6 -4
@@ -22,7 +22,7 @@ module Sequel
22
22
  end
23
23
 
24
24
  # Whether the dataset requires SQL standard datetimes (false by default,
25
- # as most allow strings with ISO 8601 format.
25
+ # as most allow strings with ISO 8601 format).
26
26
  def requires_sql_standard_datetimes?
27
27
  false
28
28
  end
@@ -3,13 +3,17 @@ module Sequel
3
3
  # ---------------------
4
4
  # :section: Methods related to dataset graphing
5
5
  # Dataset graphing changes the dataset to yield hashes where keys are table
6
- # name symbols and columns are hashes representing the values related to
6
+ # name symbols and values are hashes representing the columns related to
7
7
  # that table. All of these methods return modified copies of the receiver.
8
8
  # ---------------------
9
9
 
10
10
  # Adds the given graph aliases to the list of graph aliases to use,
11
- # unlike #set_graph_aliases, which replaces the list. See
12
- # #set_graph_aliases.
11
+ # unlike +set_graph_aliases+, which replaces the list (the equivalent
12
+ # of +select_more+ when graphing). See +set_graph_aliases+.
13
+ #
14
+ # DB[:table].add_graph_aliases(:some_alias=>[:table, :column])
15
+ # # SELECT ..., table.column AS some_alias
16
+ # # => {:table=>{:column=>some_alias_value, ...}, ...}
13
17
  def add_graph_aliases(graph_aliases)
14
18
  ds = select_more(*graph_alias_columns(graph_aliases))
15
19
  ds.opts[:graph_aliases] = (ds.opts[:graph_aliases] || (ds.opts[:graph][:column_aliases] rescue {}) || {}).merge(graph_aliases)
@@ -24,16 +28,18 @@ module Sequel
24
28
  #
25
29
  # # CREATE TABLE artists (id INTEGER, name TEXT);
26
30
  # # CREATE TABLE albums (id INTEGER, name TEXT, artist_id INTEGER);
31
+ #
27
32
  # DB[:artists].left_outer_join(:albums, :artist_id=>:id).first
28
- # => {:id=>albums.id, :name=>albums.name, :artist_id=>albums.artist_id}
33
+ # #=> {:id=>albums.id, :name=>albums.name, :artist_id=>albums.artist_id}
34
+ #
29
35
  # DB[:artists].graph(:albums, :artist_id=>:id).first
30
- # => {:artists=>{:id=>artists.id, :name=>artists.name}, :albums=>{:id=>albums.id, :name=>albums.name, :artist_id=>albums.artist_id}}
36
+ # #=> {:artists=>{:id=>artists.id, :name=>artists.name}, :albums=>{:id=>albums.id, :name=>albums.name, :artist_id=>albums.artist_id}}
31
37
  #
32
38
  # Using a join such as left_outer_join, the attribute names that are shared between
33
39
  # the tables are combined in the single return hash. You can get around that by
34
- # using .select with correct aliases for all of the columns, but it is simpler to
35
- # use graph and have the result set split for you. In addition, graph respects
36
- # any row_proc of the current dataset and the datasets you use with graph.
40
+ # using +select+ with correct aliases for all of the columns, but it is simpler to
41
+ # use +graph+ and have the result set split for you. In addition, +graph+ respects
42
+ # any +row_proc+ of the current dataset and the datasets you use with +graph+.
37
43
  #
38
44
  # If you are graphing a table and all columns for that table are nil, this
39
45
  # indicates that no matching rows existed in the table, so graph will return nil
@@ -44,26 +50,26 @@ module Sequel
44
50
  # => {:artists=>{:id=>artists.id, :name=>artists.name}, :albums=>nil}
45
51
  #
46
52
  # Arguments:
47
- # * dataset - Can be a symbol (specifying a table), another dataset,
48
- # or an object that responds to .dataset and return a symbol or a dataset
49
- # * join_conditions - Any condition(s) allowed by join_table.
50
- # * options - A hash of graph options. The following options are currently used:
51
- # * :from_self_alias - The alias to use when the receiver is not a graphed
52
- # dataset but it contains multiple FROM tables or a JOIN. In this case,
53
- # the receiver is wrapped in a from_self before graphing, and this option
54
- # determines the alias to use.
55
- # * :implicit_qualifier - The qualifier of implicit conditions, see #join_table.
56
- # * :join_type - The type of join to use (passed to join_table). Defaults to
57
- # :left_outer.
58
- # * :select - An array of columns to select. When not used, selects
59
- # all columns in the given dataset. When set to false, selects no
60
- # columns and is like simply joining the tables, though graph keeps
61
- # some metadata about join that makes it important to use graph instead
62
- # of join.
63
- # * :table_alias - The alias to use for the table. If not specified, doesn't
64
- # alias the table. You will get an error if the the alias (or table) name is
65
- # used more than once.
66
- # * block - A block that is passed to join_table.
53
+ # dataset :: Can be a symbol (specifying a table), another dataset,
54
+ # or an object that responds to +dataset+ and returns a symbol or a dataset
55
+ # join_conditions :: Any condition(s) allowed by +join_table+.
56
+ # block :: A block that is passed to +join_table+.
57
+ #
58
+ # Options:
59
+ # :from_self_alias :: The alias to use when the receiver is not a graphed
60
+ # dataset but it contains multiple FROM tables or a JOIN. In this case,
61
+ # the receiver is wrapped in a from_self before graphing, and this option
62
+ # determines the alias to use.
63
+ # :implicit_qualifier :: The qualifier of implicit conditions, see #join_table.
64
+ # :join_type :: The type of join to use (passed to +join_table+). Defaults to :left_outer.
65
+ # :select :: An array of columns to select. When not used, selects
66
+ # all columns in the given dataset. When set to false, selects no
67
+ # columns and is like simply joining the tables, though graph keeps
68
+ # some metadata about the join that makes it important to use +graph+ instead
69
+ # of +join_table+.
70
+ # :table_alias :: The alias to use for the table. If not specified, doesn't
71
+ # alias the table. You will get an error if the the alias (or table) name is
72
+ # used more than once.
67
73
  def graph(dataset, join_conditions = nil, options = {}, &block)
68
74
  # Allow the use of a model, dataset, or symbol as the first argument
69
75
  # Find the table name/dataset based on the argument
@@ -172,29 +178,31 @@ module Sequel
172
178
  # This allows you to manually specify the graph aliases to use
173
179
  # when using graph. You can use it to only select certain
174
180
  # columns, and have those columns mapped to specific aliases
175
- # in the result set. This is the equivalent of .select for a
176
- # graphed dataset, and must be used instead of .select whenever
177
- # graphing is used. Example:
181
+ # in the result set. This is the equivalent of +select+ for a
182
+ # graphed dataset, and must be used instead of +select+ whenever
183
+ # graphing is used.
178
184
  #
179
- # DB[:artists].graph(:albums, :artist_id=>:id).set_graph_aliases(:artist_name=>[:artists, :name], :album_name=>[:albums, :name], :forty_two=>[:albums, :fourtwo, 42]).first
180
- # => {:artists=>{:name=>artists.name}, :albums=>{:name=>albums.name, :fourtwo=>42}}
185
+ # graph_aliases :: Should be a hash with keys being symbols of
186
+ # column aliases, and values being arrays with two or three elements.
187
+ # The first element of the array should be the table alias symbol,
188
+ # and the second should be the actual column name symbol. If the array
189
+ # has a third element, it is used as the value returned, instead of
190
+ # table_alias.column_name.
181
191
  #
182
- # Arguments:
183
- # * graph_aliases - Should be a hash with keys being symbols of
184
- # column aliases, and values being arrays with two or three elements.
185
- # The first element of the array should be the table alias symbol,
186
- # and the second should be the actual column name symbol. If the array
187
- # has a third element, it is used as the value returned, instead of
188
- # table_alias.column_name.
192
+ # DB[:artists].graph(:albums, :artist_id=>:id).
193
+ # set_graph_aliases(:artist_name=>[:artists, :name],
194
+ # :album_name=>[:albums, :name],
195
+ # :forty_two=>[:albums, :fourtwo, 42]).first
196
+ # # SELECT artists.name AS artist_name, albums.name AS album_name, 42 AS forty_two FROM table
197
+ # # => {:artists=>{:name=>artists.name}, :albums=>{:name=>albums.name, :fourtwo=>42}}
189
198
  def set_graph_aliases(graph_aliases)
190
199
  ds = select(*graph_alias_columns(graph_aliases))
191
200
  ds.opts[:graph_aliases] = graph_aliases
192
201
  ds
193
202
  end
194
203
 
195
- # Remove the splitting of results into subhashes. Also removes
196
- # metadata related to graphing, so you should not call graph
197
- # any tables to this dataset after calling this method.
204
+ # Remove the splitting of results into subhashes, and all metadata
205
+ # related to the current graph (if any).
198
206
  def ungraphed
199
207
  clone(:graph=>nil)
200
208
  end
@@ -10,7 +10,8 @@ module Sequel
10
10
  ARG_BLOCK_ERROR_MSG = 'Must use either an argument or a block, not both'.freeze
11
11
  IMPORT_ERROR_MSG = 'Using Sequel::Dataset#import an empty column array is not allowed'.freeze
12
12
 
13
- # The database that corresponds to this dataset
13
+ # The database related to this dataset. This is the Database instance that
14
+ # will execute all of this dataset's queries.
14
15
  attr_accessor :db
15
16
 
16
17
  # The hash of options for this dataset, keys are symbols.
@@ -22,8 +23,8 @@ module Sequel
22
23
  # DB[:posts]
23
24
  #
24
25
  # Sequel::Dataset is an abstract class that is not useful by itself. Each
25
- # database adaptor should provide a subclass of Sequel::Dataset, and have
26
- # the Database#dataset method return an instance of that class.
26
+ # database adaptor provides a subclass of Sequel::Dataset, and has
27
+ # the Database#dataset method return an instance of that subclass.
27
28
  def initialize(db, opts = nil)
28
29
  @db = db
29
30
  @quote_identifiers = db.quote_identifiers? if db.respond_to?(:quote_identifiers?)
@@ -36,6 +37,8 @@ module Sequel
36
37
  # Return the dataset as an aliased expression with the given alias. You can
37
38
  # use this as a FROM or JOIN dataset, or as a column if this dataset
38
39
  # returns a single row and column.
40
+ #
41
+ # DB.from(DB[:table].as(:b)) # SELECT * FROM (SELECT * FROM table) AS b
39
42
  def as(aliaz)
40
43
  ::Sequel::SQL::AliasedExpression.new(self, aliaz)
41
44
  end
@@ -49,13 +52,19 @@ module Sequel
49
52
  db.servers.each{|s| yield server(s)}
50
53
  end
51
54
 
52
- # Alias of first_source_alias
55
+ # Alias of +first_source_alias+
53
56
  def first_source
54
57
  first_source_alias
55
58
  end
56
59
 
57
60
  # The first source (primary table) for this dataset. If the dataset doesn't
58
- # have a table, raises an error. If the table is aliased, returns the aliased name.
61
+ # have a table, raises an +Error+. If the table is aliased, returns the aliased name.
62
+ #
63
+ # DB[:table].first_source_alias
64
+ # # => :table
65
+ #
66
+ # DB[:table___t].first_source_alias
67
+ # # => :t
59
68
  def first_source_alias
60
69
  source = @opts[:from]
61
70
  if source.nil? || source.empty?
@@ -75,6 +84,12 @@ module Sequel
75
84
  # The first source (primary table) for this dataset. If the dataset doesn't
76
85
  # have a table, raises an error. If the table is aliased, returns the original
77
86
  # table, not the alias
87
+ #
88
+ # DB[:table].first_source_alias
89
+ # # => :table
90
+ #
91
+ # DB[:table___t].first_source_alias
92
+ # # => :table
78
93
  def first_source_table
79
94
  source = @opts[:from]
80
95
  if source.nil? || source.empty?
@@ -103,6 +118,15 @@ module Sequel
103
118
  # possibly appended with "_N" if the implicit alias has already been
104
119
  # used, where N is an integer starting at 0 and increasing until an
105
120
  # unused one is found.
121
+ #
122
+ # DB[:table].unused_table_alias(:t)
123
+ # # => :t
124
+ #
125
+ # DB[:table].unused_table_alias(:table)
126
+ # # => :table_0
127
+ #
128
+ # DB[:table, :table_0].unused_table_alias(:table)
129
+ # # => :table_1
106
130
  def unused_table_alias(table_alias)
107
131
  table_alias = alias_symbol(table_alias)
108
132
  used_aliases = []
@@ -20,7 +20,6 @@ module Sequel
20
20
  # Add the mutation methods via metaprogramming
21
21
  def_mutation_method(*MUTATION_METHODS)
22
22
 
23
-
24
23
  # Set the method to call on identifiers going into the database for this dataset
25
24
  attr_accessor :identifier_input_method
26
25
 
@@ -176,6 +176,10 @@ module Sequel
176
176
  # Set the bind variables to use for the call. If bind variables have
177
177
  # already been set for this dataset, they are updated with the contents
178
178
  # of bind_vars.
179
+ #
180
+ # DB[:table].filter(:id=>:$id).bind(:id=>1).call(:first)
181
+ # # SELECT * FROM table WHERE id = ? LIMIT 1 -- (1)
182
+ # # => {:id=>1}
179
183
  def bind(bind_vars={})
180
184
  clone(:bind_vars=>@opts[:bind_vars] ? @opts[:bind_vars].merge(bind_vars) : bind_vars)
181
185
  end
@@ -185,6 +189,10 @@ module Sequel
185
189
  # specified in the hash. values is a hash of passed to
186
190
  # insert or update (if one of those types is used),
187
191
  # which may contain placeholders.
192
+ #
193
+ # DB[:table].filter(:id=>:$id).call(:first, :id=>1)
194
+ # # SELECT * FROM table WHERE id = ? LIMIT 1 -- (1)
195
+ # # => {:id=>1}
188
196
  def call(type, bind_variables={}, *values, &block)
189
197
  prepare(type, nil, *values).call(bind_variables, &block)
190
198
  end
@@ -195,9 +203,13 @@ module Sequel
195
203
  # do substitution. The prepared statement is also stored in
196
204
  # the associated database. The following usage is identical:
197
205
  #
198
- # ps = prepare(:select, :select_by_name)
206
+ # ps = DB[:table].filter(:name=>:$name).prepare(:first, :select_by_name)
207
+ #
199
208
  # ps.call(:name=>'Blah')
200
- # db.call(:select_by_name, :name=>'Blah')
209
+ # # SELECT * FROM table WHERE name = ? -- ('Blah')
210
+ # # => {:id=>1, :name=>'Blah'}
211
+ #
212
+ # DB.call(:select_by_name, :name=>'Blah') # Same thing
201
213
  def prepare(type, name=nil, *values)
202
214
  ps = to_prepared_statement(type, values)
203
215
  db.prepared_statements[name] = ps if name
@@ -39,7 +39,7 @@ module Sequel
39
39
  # exists an error is raised. This method is identical to #filter except
40
40
  # it expects an existing filter.
41
41
  #
42
- # ds.filter(:a).and(:b) # SQL: WHERE a AND b
42
+ # DB[:table].filter(:a).and(:b) # SELECT * FROM table WHERE a AND b
43
43
  def and(*cond, &block)
44
44
  raise(InvalidOperation, "No existing filter found.") unless @opts[:having] || @opts[:where]
45
45
  filter(*cond, &block)
@@ -47,7 +47,8 @@ module Sequel
47
47
 
48
48
  # Returns a new clone of the dataset with with the given options merged.
49
49
  # If the options changed include options in COLUMN_CHANGE_OPTS, the cached
50
- # columns are deleted.
50
+ # columns are deleted. This method should generally not be called
51
+ # directly by user code.
51
52
  def clone(opts = {})
52
53
  c = super()
53
54
  c.opts = @opts.merge(opts)
@@ -62,8 +63,8 @@ module Sequel
62
63
  # of all returned columns. Raises an error if arguments
63
64
  # are given and DISTINCT ON is not supported.
64
65
  #
65
- # dataset.distinct # SQL: SELECT DISTINCT * FROM items
66
- # dataset.order(:id).distinct(:id) # SQL: SELECT DISTINCT ON (id) * FROM items ORDER BY id
66
+ # DB[:items].distinct # SQL: SELECT DISTINCT * FROM items
67
+ # DB[:items].order(:id).distinct(:id) # SQL: SELECT DISTINCT ON (id) * FROM items ORDER BY id
67
68
  def distinct(*args)
68
69
  raise(InvalidOperation, "DISTINCT ON not supported") if !args.empty? && !supports_distinct_on?
69
70
  clone(:distinct => args)
@@ -72,13 +73,20 @@ module Sequel
72
73
  # Adds an EXCEPT clause using a second dataset object.
73
74
  # An EXCEPT compound dataset returns all rows in the current dataset
74
75
  # that are not in the given dataset.
75
- # Raises an InvalidOperation if the operation is not supported.
76
+ # Raises an +InvalidOperation+ if the operation is not supported.
76
77
  # Options:
77
- # * :all - Set to true to use EXCEPT ALL instead of EXCEPT, so duplicate rows can occur
78
- # * :from_self - Set to false to not wrap the returned dataset in a from_self, use with care.
78
+ # :alias :: Use the given value as the from_self alias
79
+ # :all :: Set to true to use EXCEPT ALL instead of EXCEPT, so duplicate rows can occur
80
+ # :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
79
81
  #
80
- # DB[:items].except(DB[:other_items]).sql
81
- # #=> "SELECT * FROM items EXCEPT SELECT * FROM other_items"
82
+ # DB[:items].except(DB[:other_items])
83
+ # # SELECT * FROM items EXCEPT SELECT * FROM other_items
84
+ #
85
+ # DB[:items].except(DB[:other_items], :all=>true, :from_self=>false)
86
+ # # SELECT * FROM items EXCEPT ALL SELECT * FROM other_items
87
+ #
88
+ # DB[:items].except(DB[:other_items], :alias=>:i)
89
+ # # SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS i
82
90
  def except(dataset, opts={})
83
91
  opts = {:all=>opts} unless opts.is_a?(Hash)
84
92
  raise(InvalidOperation, "EXCEPT not supported") unless supports_intersect_except?
@@ -86,10 +94,14 @@ module Sequel
86
94
  compound_clone(:except, dataset, opts)
87
95
  end
88
96
 
89
- # Performs the inverse of Dataset#filter.
97
+ # Performs the inverse of Dataset#filter. Note that if you have multiple filter
98
+ # conditions, this is not the same as a negation of all conditions.
90
99
  #
91
- # dataset.exclude(:category => 'software').sql #=>
92
- # "SELECT * FROM items WHERE (category != 'software')"
100
+ # DB[:items].exclude(:category => 'software')
101
+ # # SELECT * FROM items WHERE (category != 'software')
102
+ #
103
+ # DB[:items].exclude(:category => 'software', :id=>3)
104
+ # # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
93
105
  def exclude(*cond, &block)
94
106
  clause = (@opts[:having] ? :having : :where)
95
107
  cond = cond.first if cond.size == 1
@@ -112,6 +124,7 @@ module Sequel
112
124
  # * If all members are arrays of length two, treats the same way
113
125
  # as a hash, except it allows for duplicate keys to be
114
126
  # specified.
127
+ # * Otherwise, treats each argument as a separate condition.
115
128
  # * String - taken literally
116
129
  # * Symbol - taken as a boolean column argument (e.g. WHERE active)
117
130
  # * Sequel::SQL::BooleanExpression - an existing condition expression,
@@ -122,29 +135,32 @@ module Sequel
122
135
  # which is easy to use to create identifiers and functions. For more details
123
136
  # on the virtual row support, see the {"Virtual Rows" guide}[link:files/doc/virtual_rows_rdoc.html]
124
137
  #
125
- # If both a block and regular argument
126
- # are provided, they get ANDed together.
138
+ # If both a block and regular argument are provided, they get ANDed together.
127
139
  #
128
140
  # Examples:
129
141
  #
130
- # dataset.filter(:id => 3).sql #=>
131
- # "SELECT * FROM items WHERE (id = 3)"
132
- # dataset.filter('price < ?', 100).sql #=>
133
- # "SELECT * FROM items WHERE price < 100"
134
- # dataset.filter([[:id, (1,2,3)], [:id, 0..10]]).sql #=>
135
- # "SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))"
136
- # dataset.filter('price < 100').sql #=>
137
- # "SELECT * FROM items WHERE price < 100"
138
- # dataset.filter(:active).sql #=>
139
- # "SELECT * FROM items WHERE :active
140
- # dataset.filter{|o| o.price < 100}.sql #=>
141
- # "SELECT * FROM items WHERE (price < 100)"
142
+ # DB[:items].filter(:id => 3)
143
+ # # SELECT * FROM items WHERE (id = 3)
144
+ #
145
+ # DB[:items].filter('price < ?', 100)
146
+ # # SELECT * FROM items WHERE price < 100
147
+ #
148
+ # DB[:items].filter([[:id, (1,2,3)], [:id, 0..10]])
149
+ # # SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))
150
+ #
151
+ # DB[:items].filter('price < 100')
152
+ # # SELECT * FROM items WHERE price < 100
153
+ #
154
+ # DB[:items].filter(:active)
155
+ # # SELECT * FROM items WHERE :active
156
+ #
157
+ # DB[:items].filter{price < 100}
158
+ # # SELECT * FROM items WHERE (price < 100)
142
159
  #
143
160
  # Multiple filter calls can be chained for scoping:
144
161
  #
145
- # software = dataset.filter(:category => 'software')
146
- # software.filter{|o| o.price < 100}.sql #=>
147
- # "SELECT * FROM items WHERE ((category = 'software') AND (price < 100))"
162
+ # software = dataset.filter(:category => 'software').filter{price < 100}
163
+ # # SELECT * FROM items WHERE ((category = 'software') AND (price < 100))
148
164
  #
149
165
  # See the the {"Dataset Filtering" guide}[link:files/doc/dataset_filtering_rdoc.html] for more examples and details.
150
166
  def filter(*cond, &block)
@@ -152,15 +168,19 @@ module Sequel
152
168
  end
153
169
 
154
170
  # Returns a cloned dataset with a :update lock style.
171
+ #
172
+ # DB[:table].for_update # SELECT * FROM table FOR UPDATE
155
173
  def for_update
156
174
  lock_style(:update)
157
175
  end
158
176
 
159
- # Returns a copy of the dataset with the source changed.
177
+ # Returns a copy of the dataset with the source changed. If no
178
+ # source is given, removes all tables. If multiple sources
179
+ # are given, it is the same as using a CROSS JOIN (cartesian product) between all tables.
160
180
  #
161
- # dataset.from # SQL: SELECT *
162
- # dataset.from(:blah) # SQL: SELECT * FROM blah
163
- # dataset.from(:blah, :foo) # SQL: SELECT * FROM blah, foo
181
+ # DB[:items].from # SQL: SELECT *
182
+ # DB[:items].from(:blah) # SQL: SELECT * FROM blah
183
+ # DB[:items].from(:blah, :foo) # SQL: SELECT * FROM blah, foo
164
184
  def from(*source)
165
185
  table_alias_num = 0
166
186
  sources = []
@@ -188,35 +208,72 @@ module Sequel
188
208
  end
189
209
 
190
210
  # Returns a dataset selecting from the current dataset.
191
- # Supplying the :alias option controls the name of the result.
211
+ # Supplying the :alias option controls the alias of the result.
192
212
  #
193
213
  # ds = DB[:items].order(:name).select(:id, :name)
194
- # ds.sql #=> "SELECT id,name FROM items ORDER BY name"
195
- # ds.from_self.sql #=> "SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS t1"
196
- # ds.from_self(:alias=>:foo).sql #=> "SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo"
214
+ # # SELECT id,name FROM items ORDER BY name
215
+ #
216
+ # ds.from_self
217
+ # # SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS t1
218
+ #
219
+ # ds.from_self(:alias=>:foo)
220
+ # # SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo
197
221
  def from_self(opts={})
198
222
  fs = {}
199
223
  @opts.keys.each{|k| fs[k] = nil unless NON_SQL_OPTIONS.include?(k)}
200
224
  clone(fs).from(opts[:alias] ? as(opts[:alias]) : self)
201
225
  end
202
226
 
203
- # Pattern match any of the columns to any of the terms. The terms can be
204
- # strings (which use LIKE) or regular expressions (which are only supported
205
- # in some databases). See Sequel::SQL::StringExpression.like. Note that the
206
- # total number of pattern matches will be cols.length * terms.length,
227
+ # Match any of the columns to any of the patterns. The terms can be
228
+ # strings (which use LIKE) or regular expressions (which are only
229
+ # supported on MySQL and PostgreSQL). Note that the total number of
230
+ # pattern matches will be Array(columns).length * Array(terms).length,
207
231
  # which could cause performance issues.
208
232
  #
209
- # dataset.grep(:a, '%test%') # SQL: SELECT * FROM items WHERE a LIKE '%test%'
210
- # dataset.grep([:a, :b], %w'%test% foo') # SQL: SELECT * FROM items WHERE a LIKE '%test%' OR a LIKE 'foo' OR b LIKE '%test%' OR b LIKE 'foo'
211
- def grep(cols, terms)
212
- filter(SQL::BooleanExpression.new(:OR, *Array(cols).collect{|c| SQL::StringExpression.like(c, *terms)}))
233
+ # Options (all are boolean):
234
+ #
235
+ # :all_columns :: All columns must be matched to any of the given patterns.
236
+ # :all_patterns :: All patterns must match at least one of the columns.
237
+ # :case_insensitive :: Use a case insensitive pattern match (the default is
238
+ # case sensitive if the database supports it).
239
+ #
240
+ # If both :all_columns and :all_patterns are true, all columns must match all patterns.
241
+ #
242
+ # Examples:
243
+ #
244
+ # dataset.grep(:a, '%test%')
245
+ # # SELECT * FROM items WHERE (a LIKE '%test%')
246
+ #
247
+ # dataset.grep([:a, :b], %w'%test% foo')
248
+ # # SELECT * FROM items WHERE ((a LIKE '%test%') OR (a LIKE 'foo') OR (b LIKE '%test%') OR (b LIKE 'foo'))
249
+ #
250
+ # dataset.grep([:a, :b], %w'%foo% %bar%', :all_patterns=>true)
251
+ # # SELECT * FROM a WHERE (((a LIKE '%foo%') OR (b LIKE '%foo%')) AND ((a LIKE '%bar%') OR (b LIKE '%bar%')))
252
+ #
253
+ # dataset.grep([:a, :b], %w'%foo% %bar%', :all_columns=>true)
254
+ # # SELECT * FROM a WHERE (((a LIKE '%foo%') OR (a LIKE '%bar%')) AND ((b LIKE '%foo%') OR (b LIKE '%bar%')))
255
+ #
256
+ # dataset.grep([:a, :b], %w'%foo% %bar%', :all_patterns=>true, :all_columns=>true)
257
+ # # SELECT * FROM a WHERE ((a LIKE '%foo%') AND (b LIKE '%foo%') AND (a LIKE '%bar%') AND (b LIKE '%bar%'))
258
+ def grep(columns, patterns, opts={})
259
+ if opts[:all_patterns]
260
+ conds = Array(patterns).map do |pat|
261
+ SQL::BooleanExpression.new(opts[:all_columns] ? :AND : :OR, *Array(columns).map{|c| SQL::StringExpression.like(c, pat, opts)})
262
+ end
263
+ filter(SQL::BooleanExpression.new(opts[:all_patterns] ? :AND : :OR, *conds))
264
+ else
265
+ conds = Array(columns).map do |c|
266
+ SQL::BooleanExpression.new(:OR, *Array(patterns).map{|pat| SQL::StringExpression.like(c, pat, opts)})
267
+ end
268
+ filter(SQL::BooleanExpression.new(opts[:all_columns] ? :AND : :OR, *conds))
269
+ end
213
270
  end
214
271
 
215
272
  # Returns a copy of the dataset with the results grouped by the value of
216
273
  # the given columns.
217
274
  #
218
- # dataset.group(:id) # SELECT * FROM items GROUP BY id
219
- # dataset.group(:id, :name) # SELECT * FROM items GROUP BY id, name
275
+ # DB[:items].group(:id) # SELECT * FROM items GROUP BY id
276
+ # DB[:items].group(:id, :name) # SELECT * FROM items GROUP BY id, name
220
277
  def group(*columns)
221
278
  clone(:group => (columns.compact.empty? ? nil : columns))
222
279
  end
@@ -231,16 +288,25 @@ module Sequel
231
288
  #
232
289
  # Examples:
233
290
  #
234
- # ds.group_and_count(:name).all => [{:name=>'a', :count=>1}, ...]
235
- # ds.group_and_count(:first_name, :last_name).all => [{:first_name=>'a', :last_name=>'b', :count=>1}, ...]
236
- # ds.group_and_count(:first_name___name).all => [{:name=>'a', :count=>1}, ...]
291
+ # DB[:items].group_and_count(:name).all
292
+ # # SELECT name, count(*) AS count FROM items GROUP BY name
293
+ # # => [{:name=>'a', :count=>1}, ...]
294
+ #
295
+ # DB[:items].group_and_count(:first_name, :last_name).all
296
+ # # SELECT first_name, last_name, count(*) AS count FROM items GROUP BY first_name, last_name
297
+ # # => [{:first_name=>'a', :last_name=>'b', :count=>1}, ...]
298
+ #
299
+ # DB[:items].group_and_count(:first_name___name).all
300
+ # # SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name
301
+ # # => [{:name=>'a', :count=>1}, ...]
237
302
  def group_and_count(*columns)
238
303
  group(*columns.map{|c| unaliased_identifier(c)}).select(*(columns + [COUNT_OF_ALL_AS_COUNT]))
239
304
  end
240
305
 
241
306
  # Returns a copy of the dataset with the HAVING conditions changed. See #filter for argument types.
242
307
  #
243
- # dataset.group(:sum).having(:sum=>10) # SQL: SELECT * FROM items GROUP BY sum HAVING sum = 10
308
+ # DB[:items].group(:sum).having(:sum=>10)
309
+ # # SELECT * FROM items GROUP BY sum HAVING (sum = 10)
244
310
  def having(*cond, &block)
245
311
  _filter(:having, *cond, &block)
246
312
  end
@@ -248,13 +314,20 @@ module Sequel
248
314
  # Adds an INTERSECT clause using a second dataset object.
249
315
  # An INTERSECT compound dataset returns all rows in both the current dataset
250
316
  # and the given dataset.
251
- # Raises an InvalidOperation if the operation is not supported.
317
+ # Raises an +InvalidOperation+ if the operation is not supported.
252
318
  # Options:
253
- # * :all - Set to true to use INTERSECT ALL instead of INTERSECT, so duplicate rows can occur
254
- # * :from_self - Set to false to not wrap the returned dataset in a from_self, use with care.
319
+ # :alias :: Use the given value as the from_self alias
320
+ # :all :: Set to true to use INTERSECT ALL instead of INTERSECT, so duplicate rows can occur
321
+ # :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
255
322
  #
256
- # DB[:items].intersect(DB[:other_items]).sql
257
- # #=> "SELECT * FROM items INTERSECT SELECT * FROM other_items"
323
+ # DB[:items].intersect(DB[:other_items])
324
+ # # SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS t1
325
+ #
326
+ # DB[:items].intersect(DB[:other_items], :all=>true, :from_self=>false)
327
+ # # SELECT * FROM items INTERSECT ALL SELECT * FROM other_items
328
+ #
329
+ # DB[:items].intersect(DB[:other_items], :alias=>:i)
330
+ # # SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS i
258
331
  def intersect(dataset, opts={})
259
332
  opts = {:all=>opts} unless opts.is_a?(Hash)
260
333
  raise(InvalidOperation, "INTERSECT not supported") unless supports_intersect_except?
@@ -262,10 +335,13 @@ module Sequel
262
335
  compound_clone(:intersect, dataset, opts)
263
336
  end
264
337
 
265
- # Inverts the current filter
338
+ # Inverts the current filter.
339
+ #
340
+ # DB[:items].filter(:category => 'software').invert
341
+ # # SELECT * FROM items WHERE (category != 'software')
266
342
  #
267
- # dataset.filter(:category => 'software').invert.sql #=>
268
- # "SELECT * FROM items WHERE (category != 'software')"
343
+ # DB[:items].filter(:category => 'software', :id=>3).invert
344
+ # # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
269
345
  def invert
270
346
  having, where = @opts[:having], @opts[:where]
271
347
  raise(Error, "No current filter") unless having || where
@@ -275,7 +351,7 @@ module Sequel
275
351
  clone(o)
276
352
  end
277
353
 
278
- # Alias of inner_join
354
+ # Alias of +inner_join+
279
355
  def join(*args, &block)
280
356
  inner_join(*args, &block)
281
357
  end
@@ -288,7 +364,7 @@ module Sequel
288
364
  # * Model (or anything responding to :table_name) - table.table_name
289
365
  # * String, Symbol: table
290
366
  # * expr - specifies conditions, depends on type:
291
- # * Hash, Array with all two pairs - Assumes key (1st arg) is column of joined table (unless already
367
+ # * Hash, Array of two element arrays - Assumes key (1st arg) is column of joined table (unless already
292
368
  # qualified), and value (2nd arg) is column of the last joined or primary table (or the
293
369
  # :implicit_qualifier option).
294
370
  # To specify multiple conditions on a single joined table column, you must use an array.
@@ -297,10 +373,10 @@ module Sequel
297
373
  # uses a JOIN with a USING clause. Most databases will remove duplicate columns from
298
374
  # the result set if this is used.
299
375
  # * nil - If a block is not given, doesn't use ON or USING, so the JOIN should be a NATURAL
300
- # or CROSS join. If a block is given, uses a ON clause based on the block, see below.
376
+ # or CROSS join. If a block is given, uses an ON clause based on the block, see below.
301
377
  # * Everything else - pretty much the same as a using the argument in a call to filter,
302
- # so strings are considered literal, symbols specify boolean columns, and blockless
303
- # filter expressions can be used. Uses a JOIN with an ON clause.
378
+ # so strings are considered literal, symbols specify boolean columns, and Sequel
379
+ # expressions can be used. Uses a JOIN with an ON clause.
304
380
  # * options - a hash of options, with any of the following keys:
305
381
  # * :table_alias - the name of the table's alias when joining, necessary for joining
306
382
  # to the same table more than once. No alias is used by default.
@@ -309,7 +385,7 @@ module Sequel
309
385
  # * block - The block argument should only be given if a JOIN with an ON clause is used,
310
386
  # in which case it yields the table alias/name for the table currently being joined,
311
387
  # the table alias/name for the last joined (or first table), and an array of previous
312
- # SQL::JoinClause.
388
+ # SQL::JoinClause. Unlike +filter+, this block is not treated as a virtual row block.
313
389
  def join_table(type, table, expr=nil, options={}, &block)
314
390
  using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
315
391
  if using_join && !supports_join_using?
@@ -375,10 +451,14 @@ module Sequel
375
451
 
376
452
  # If given an integer, the dataset will contain only the first l results.
377
453
  # If given a range, it will contain only those at offsets within that
378
- # range. If a second argument is given, it is used as an offset.
379
- #
380
- # dataset.limit(10) # SQL: SELECT * FROM items LIMIT 10
381
- # dataset.limit(10, 20) # SQL: SELECT * FROM items LIMIT 10 OFFSET 20
454
+ # range. If a second argument is given, it is used as an offset. To use
455
+ # an offset without a limit, pass nil as the first argument.
456
+ #
457
+ # DB[:items].limit(10) # SELECT * FROM items LIMIT 10
458
+ # DB[:items].limit(10, 20) # SELECT * FROM items LIMIT 10 OFFSET 20
459
+ # DB[:items].limit(10...20) # SELECT * FROM items LIMIT 10 OFFSET 10
460
+ # DB[:items].limit(10..20) # SELECT * FROM items LIMIT 11 OFFSET 10
461
+ # DB[:items].limit(nil, 20) # SELECT * FROM items OFFSET 20
382
462
  def limit(l, o = nil)
383
463
  return from_self.limit(l, o) if @opts[:sql]
384
464
 
@@ -405,12 +485,18 @@ module Sequel
405
485
  # string, it will be used directly. Otherwise, a symbol may be used
406
486
  # for database independent locking. Currently :update is respected
407
487
  # by most databases, and :share is supported by some.
488
+ #
489
+ # DB[:items].lock_style('FOR SHARE') # SELECT * FROM items FOR SHARE
408
490
  def lock_style(style)
409
491
  clone(:lock => style)
410
492
  end
411
493
 
412
- # Returns a naked dataset clone - i.e. a dataset that returns records as
413
- # hashes instead of calling the row proc.
494
+ # Returns a cloned dataset without a row_proc.
495
+ #
496
+ # ds = DB[:items]
497
+ # ds.row_proc = proc{|r| r.invert}
498
+ # ds.all # => [{2=>:id}]
499
+ # ds.naked.all # => [{:id=>2}]
414
500
  def naked
415
501
  ds = clone
416
502
  ds.row_proc = nil
@@ -418,9 +504,9 @@ module Sequel
418
504
  end
419
505
 
420
506
  # Adds an alternate filter to an existing filter using OR. If no filter
421
- # exists an error is raised.
507
+ # exists an +Error+ is raised.
422
508
  #
423
- # dataset.filter(:a).or(:b) # SQL: SELECT * FROM items WHERE a OR b
509
+ # DB[:items].filter(:a).or(:b) # SELECT * FROM items WHERE a OR b
424
510
  def or(*cond, &block)
425
511
  clause = (@opts[:having] ? :having : :where)
426
512
  raise(InvalidOperation, "No existing filter found.") unless @opts[clause]
@@ -428,19 +514,20 @@ module Sequel
428
514
  clone(clause => SQL::BooleanExpression.new(:OR, @opts[clause], filter_expr(cond, &block)))
429
515
  end
430
516
 
431
- # Returns a copy of the dataset with the order changed. If a nil is given
517
+ # Returns a copy of the dataset with the order changed. If the dataset has an
518
+ # existing order, it is ignored and overwritten with this order. If a nil is given
432
519
  # the returned dataset has no order. This can accept multiple arguments
433
- # of varying kinds, and even SQL functions. If a block is given, it is treated
434
- # as a virtual row block, similar to filter.
435
- #
436
- # ds.order(:name).sql #=> 'SELECT * FROM items ORDER BY name'
437
- # ds.order(:a, :b).sql #=> 'SELECT * FROM items ORDER BY a, b'
438
- # ds.order('a + b'.lit).sql #=> 'SELECT * FROM items ORDER BY a + b'
439
- # ds.order(:a + :b).sql #=> 'SELECT * FROM items ORDER BY (a + b)'
440
- # ds.order(:name.desc).sql #=> 'SELECT * FROM items ORDER BY name DESC'
441
- # ds.order(:name.asc).sql #=> 'SELECT * FROM items ORDER BY name ASC'
442
- # ds.order{|o| o.sum(:name)}.sql #=> 'SELECT * FROM items ORDER BY sum(name)'
443
- # ds.order(nil).sql #=> 'SELECT * FROM items'
520
+ # of varying kinds, such as SQL functions. If a block is given, it is treated
521
+ # as a virtual row block, similar to +filter+.
522
+ #
523
+ # DB[:items].order(:name) # SELECT * FROM items ORDER BY name
524
+ # DB[:items].order(:a, :b) # SELECT * FROM items ORDER BY a, b
525
+ # DB[:items].order('a + b'.lit) # SELECT * FROM items ORDER BY a + b
526
+ # DB[:items].order(:a + :b) # SELECT * FROM items ORDER BY (a + b)
527
+ # DB[:items].order(:name.desc) # SELECT * FROM items ORDER BY name DESC
528
+ # DB[:items].order(:name.asc(:nulls=>:last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
529
+ # DB[:items].order{sum(name).desc} # SELECT * FROM items ORDER BY sum(name) DESC
530
+ # DB[:items].order(nil) # SELECT * FROM items
444
531
  def order(*columns, &block)
445
532
  columns += Array(Sequel.virtual_row(&block)) if block
446
533
  clone(:order => (columns.compact.empty?) ? nil : columns)
@@ -459,8 +546,8 @@ module Sequel
459
546
  # Returns a copy of the dataset with the order columns added
460
547
  # to the end of the existing order.
461
548
  #
462
- # ds.order(:a).order(:b).sql #=> 'SELECT * FROM items ORDER BY b'
463
- # ds.order(:a).order_more(:b).sql #=> 'SELECT * FROM items ORDER BY a, b'
549
+ # DB[:items].order(:a).order(:b) # SELECT * FROM items ORDER BY b
550
+ # DB[:items].order(:a).order_more(:b) # SELECT * FROM items ORDER BY a, b
464
551
  def order_more(*columns, &block)
465
552
  columns = @opts[:order] + columns if @opts[:order]
466
553
  order(*columns, &block)
@@ -469,14 +556,20 @@ module Sequel
469
556
  # Returns a copy of the dataset with the order columns added
470
557
  # to the beginning of the existing order.
471
558
  #
472
- # ds.order(:a).order(:b).sql #=> 'SELECT * FROM items ORDER BY b'
473
- # ds.order(:a).order_prepend(:b).sql #=> 'SELECT * FROM items ORDER BY b, a'
559
+ # DB[:items].order(:a).order(:b) # SELECT * FROM items ORDER BY b
560
+ # DB[:items].order(:a).order_prepend(:b) # SELECT * FROM items ORDER BY b, a
474
561
  def order_prepend(*columns, &block)
475
562
  ds = order(*columns, &block)
476
563
  @opts[:order] ? ds.order_more(*@opts[:order]) : ds
477
564
  end
478
565
 
479
566
  # Qualify to the given table, or first source if not table is given.
567
+ #
568
+ # DB[:items].filter(:id=>1).qualify
569
+ # # SELECT items.* FROM items WHERE (items.id = 1)
570
+ #
571
+ # DB[:items].filter(:id=>1).qualify(:i)
572
+ # # SELECT i.* FROM items WHERE (i.id = 1)
480
573
  def qualify(table=first_source)
481
574
  qualify_to(table)
482
575
  end
@@ -485,6 +578,9 @@ module Sequel
485
578
  # SELECT, WHERE, GROUP, HAVING, and ORDER clauses qualified by the
486
579
  # given table. If no columns are currently selected, select all
487
580
  # columns of the given table.
581
+ #
582
+ # DB[:items].filter(:id=>1).qualify_to(:i)
583
+ # # SELECT i.* FROM items WHERE (i.id = 1)
488
584
  def qualify_to(table)
489
585
  o = @opts
490
586
  return clone if o[:sql]
@@ -500,29 +596,36 @@ module Sequel
500
596
  # if you have unqualified identifiers in the query that all refer to
501
597
  # the first source, and you want to join to another table which
502
598
  # has columns with the same name as columns in the current dataset.
503
- # See qualify_to.
599
+ # See +qualify_to+.
600
+ #
601
+ # DB[:items].filter(:id=>1).qualify_to_first_source
602
+ # # SELECT items.* FROM items WHERE (items.id = 1)
504
603
  def qualify_to_first_source
505
604
  qualify_to(first_source)
506
605
  end
507
606
 
508
607
  # Returns a copy of the dataset with the order reversed. If no order is
509
608
  # given, the existing order is inverted.
609
+ #
610
+ # DB[:items].reverse(:id) # SELECT * FROM items ORDER BY id DESC
611
+ # DB[:items].order(:id).reverse # SELECT * FROM items ORDER BY id DESC
612
+ # DB[:items].order(:id).reverse(:name.asc) # SELECT * FROM items ORDER BY name ASC
510
613
  def reverse(*order)
511
614
  order(*invert_order(order.empty? ? @opts[:order] : order))
512
615
  end
513
616
 
514
- # Alias of reverse
617
+ # Alias of +reverse+
515
618
  def reverse_order(*order)
516
619
  reverse(*order)
517
620
  end
518
621
 
519
622
  # Returns a copy of the dataset with the columns selected changed
520
623
  # to the given columns. This also takes a virtual row block,
521
- # similar to filter.
624
+ # similar to +filter+.
522
625
  #
523
- # dataset.select(:a) # SELECT a FROM items
524
- # dataset.select(:a, :b) # SELECT a, b FROM items
525
- # dataset.select{|o| [o.a, o.sum(:b)]} # SELECT a, sum(b) FROM items
626
+ # DB[:items].select(:a) # SELECT a FROM items
627
+ # DB[:items].select(:a, :b) # SELECT a, b FROM items
628
+ # DB[:items].select{[a, sum(b)]} # SELECT a, sum(b) FROM items
526
629
  def select(*columns, &block)
527
630
  columns += Array(Sequel.virtual_row(&block)) if block
528
631
  m = []
@@ -534,18 +637,18 @@ module Sequel
534
637
 
535
638
  # Returns a copy of the dataset selecting the wildcard.
536
639
  #
537
- # dataset.select(:a).select_all # SELECT * FROM items
640
+ # DB[:items].select(:a).select_all # SELECT * FROM items
538
641
  def select_all
539
642
  clone(:select => nil)
540
643
  end
541
644
 
542
645
  # Returns a copy of the dataset with the given columns added
543
- # to the existing selected columns. If no columns are currently selected
646
+ # to the existing selected columns. If no columns are currently selected,
544
647
  # it will select the columns given in addition to *.
545
648
  #
546
- # dataset.select(:a).select(:b) # SELECT b FROM items
547
- # dataset.select(:a).select_append(:b) # SELECT a, b FROM items
548
- # dataset.select_append(:b) # SELECT *, b FROM items
649
+ # DB[:items].select(:a).select(:b) # SELECT b FROM items
650
+ # DB[:items].select(:a).select_append(:b) # SELECT a, b FROM items
651
+ # DB[:items].select_append(:b) # SELECT *, b FROM items
549
652
  def select_append(*columns, &block)
550
653
  cur_sel = @opts[:select]
551
654
  cur_sel = [WILDCARD] if !cur_sel || cur_sel.empty?
@@ -556,9 +659,9 @@ module Sequel
556
659
  # to the existing selected columns. If no columns are currently selected
557
660
  # it will just select the columns given.
558
661
  #
559
- # dataset.select(:a).select(:b) # SELECT b FROM items
560
- # dataset.select(:a).select_more(:b) # SELECT a, b FROM items
561
- # dataset.select_more(:b) # SELECT b FROM items
662
+ # DB[:items].select(:a).select(:b) # SELECT b FROM items
663
+ # DB[:items].select(:a).select_more(:b) # SELECT a, b FROM items
664
+ # DB[:items].select_more(:b) # SELECT b FROM items
562
665
  def select_more(*columns, &block)
563
666
  columns = @opts[:select] + columns if @opts[:select]
564
667
  select(*columns, &block)
@@ -566,33 +669,49 @@ module Sequel
566
669
 
567
670
  # Set the server for this dataset to use. Used to pick a specific database
568
671
  # shard to run a query against, or to override the default (which is SELECT uses
569
- # :read_only database and all other queries use the :default database).
672
+ # :read_only database and all other queries use the :default database). This
673
+ # method is always available but is only useful when database sharding is being
674
+ # used.
675
+ #
676
+ # DB[:items].all # Uses the :read_only or :default server
677
+ # DB[:items].delete # Uses the :default server
678
+ # DB[:items].server(:blah).delete # Uses the :blah server
570
679
  def server(servr)
571
680
  clone(:server=>servr)
572
681
  end
573
682
 
574
683
  # Set the default values for insert and update statements. The values hash passed
575
- # to insert or update are merged into this hash.
684
+ # to insert or update are merged into this hash, so any values in the hash passed
685
+ # to insert or update will override values passed to this method.
686
+ #
687
+ # DB[:items].set_defaults(:a=>'a', :c=>'c').insert(:a=>'d', :b=>'b')
688
+ # # INSERT INTO items (a, c, b) VALUES ('d', 'c', 'b')
576
689
  def set_defaults(hash)
577
690
  clone(:defaults=>(@opts[:defaults]||{}).merge(hash))
578
691
  end
579
692
 
580
693
  # Set values that override hash arguments given to insert and update statements.
581
- # This hash is merged into the hash provided to insert or update.
694
+ # This hash is merged into the hash provided to insert or update, so values
695
+ # will override any values given in the insert/update hashes.
696
+ #
697
+ # DB[:items].set_overrides(:a=>'a', :c=>'c').insert(:a=>'d', :b=>'b')
698
+ # # INSERT INTO items (a, c, b) VALUES ('a', 'c', 'b')
582
699
  def set_overrides(hash)
583
700
  clone(:overrides=>hash.merge(@opts[:overrides]||{}))
584
701
  end
585
702
 
586
703
  # Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
587
704
  #
588
- # dataset.group(:a).having(:a=>1).where(:b).unfiltered # SELECT * FROM items GROUP BY a
705
+ # DB[:items].group(:a).having(:a=>1).where(:b).unfiltered
706
+ # # SELECT * FROM items GROUP BY a
589
707
  def unfiltered
590
708
  clone(:where => nil, :having => nil)
591
709
  end
592
710
 
593
711
  # Returns a copy of the dataset with no grouping (GROUP or HAVING clause) applied.
594
712
  #
595
- # dataset.group(:a).having(:a=>1).where(:b).ungrouped # SELECT * FROM items WHERE b
713
+ # DB[:items].group(:a).having(:a=>1).where(:b).ungrouped
714
+ # # SELECT * FROM items WHERE b
596
715
  def ungrouped
597
716
  clone(:group => nil, :having => nil)
598
717
  end
@@ -601,11 +720,18 @@ module Sequel
601
720
  # A UNION compound dataset returns all rows in either the current dataset
602
721
  # or the given dataset.
603
722
  # Options:
604
- # * :all - Set to true to use UNION ALL instead of UNION, so duplicate rows can occur
605
- # * :from_self - Set to false to not wrap the returned dataset in a from_self, use with care.
723
+ # :alias :: Use the given value as the from_self alias
724
+ # :all :: Set to true to use UNION ALL instead of UNION, so duplicate rows can occur
725
+ # :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
606
726
  #
607
727
  # DB[:items].union(DB[:other_items]).sql
608
728
  # #=> "SELECT * FROM items UNION SELECT * FROM other_items"
729
+ #
730
+ # DB[:items].union(DB[:other_items], :all=>true, :from_self=>false)
731
+ # # SELECT * FROM items UNION ALL SELECT * FROM other_items
732
+ #
733
+ # DB[:items].union(DB[:other_items], :alias=>:i)
734
+ # # SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS i
609
735
  def union(dataset, opts={})
610
736
  opts = {:all=>opts} unless opts.is_a?(Hash)
611
737
  compound_clone(:union, dataset, opts)
@@ -613,31 +739,37 @@ module Sequel
613
739
 
614
740
  # Returns a copy of the dataset with no limit or offset.
615
741
  #
616
- # dataset.limit(10, 20).unlimited # SELECT * FROM items
742
+ # DB[:items].limit(10, 20).unlimited # SELECT * FROM items
617
743
  def unlimited
618
744
  clone(:limit=>nil, :offset=>nil)
619
745
  end
620
746
 
621
747
  # Returns a copy of the dataset with no order.
622
748
  #
623
- # dataset.order(:a).unordered # SELECT * FROM items
749
+ # DB[:items].order(:a).unordered # SELECT * FROM items
624
750
  def unordered
625
751
  order(nil)
626
752
  end
627
753
 
628
- # Add a condition to the WHERE clause. See #filter for argument types.
754
+ # Add a condition to the WHERE clause. See +filter+ for argument types.
629
755
  #
630
- # dataset.group(:a).having(:a).filter(:b) # SELECT * FROM items GROUP BY a HAVING a AND b
631
- # dataset.group(:a).having(:a).where(:b) # SELECT * FROM items WHERE b GROUP BY a HAVING a
756
+ # DB[:items].group(:a).having(:a).filter(:b)
757
+ # # SELECT * FROM items GROUP BY a HAVING a AND b
758
+ #
759
+ # DB[:items].group(:a).having(:a).where(:b)
760
+ # # SELECT * FROM items WHERE b GROUP BY a HAVING a
632
761
  def where(*cond, &block)
633
762
  _filter(:where, *cond, &block)
634
763
  end
635
764
 
636
- # Add a simple common table expression (CTE) with the given name and a dataset that defines the CTE.
765
+ # Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
637
766
  # A common table expression acts as an inline view for the query.
638
767
  # Options:
639
- # * :args - Specify the arguments/columns for the CTE, should be an array of symbols.
640
- # * :recursive - Specify that this is a recursive CTE
768
+ # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
769
+ # :recursive :: Specify that this is a recursive CTE
770
+ #
771
+ # DB[:items].with(:items, DB[:syx].filter(:name.like('A%')))
772
+ # # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%')) SELECT * FROM items
641
773
  def with(name, dataset, opts={})
642
774
  raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
643
775
  clone(:with=>(@opts[:with]||[]) + [opts.merge(:name=>name, :dataset=>dataset)])
@@ -646,8 +778,20 @@ module Sequel
646
778
  # Add a recursive common table expression (CTE) with the given name, a dataset that
647
779
  # defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
648
780
  # of the CTE. Options:
649
- # * :args - Specify the arguments/columns for the CTE, should be an array of symbols.
650
- # * :union_all - Set to false to use UNION instead of UNION ALL combining the nonrecursive and recursive parts.
781
+ # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
782
+ # :union_all :: Set to false to use UNION instead of UNION ALL combining the nonrecursive and recursive parts.
783
+ #
784
+ # DB[:t].select(:i___id, :pi___parent_id).
785
+ # with_recursive(:t,
786
+ # DB[:i1].filter(:parent_id=>nil),
787
+ # DB[:t].join(:t, :i=>:parent_id).select(:i1__id, :i1__parent_id),
788
+ # :args=>[:i, :pi])
789
+ # # WITH RECURSIVE t(i, pi) AS (
790
+ # # SELECT * FROM i1 WHERE (parent_id IS NULL)
791
+ # # UNION ALL
792
+ # # SELECT i1.id, i1.parent_id FROM t INNER JOIN t ON (t.i = t.parent_id)
793
+ # # )
794
+ # # SELECT i AS id, pi AS parent_id FROM t
651
795
  def with_recursive(name, nonrecursive, recursive, opts={})
652
796
  raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
653
797
  clone(:with=>(@opts[:with]||[]) + [opts.merge(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))])
@@ -656,7 +800,7 @@ module Sequel
656
800
  # Returns a copy of the dataset with the static SQL used. This is useful if you want
657
801
  # to keep the same row_proc/graph, but change the SQL used to custom SQL.
658
802
  #
659
- # dataset.with_sql('SELECT * FROM foo') # SELECT * FROM foo
803
+ # DB[:items].with_sql('SELECT * FROM foo') # SELECT * FROM foo
660
804
  def with_sql(sql, *args)
661
805
  sql = SQL::PlaceholderLiteralString.new(sql, args) unless args.empty?
662
806
  clone(:sql=>sql)
@@ -695,7 +839,7 @@ module Sequel
695
839
  opts[:from_self] == false ? ds : ds.from_self(opts)
696
840
  end
697
841
 
698
- # SQL fragment based on the expr type. See #filter.
842
+ # SQL expression object based on the expr type. See +filter+.
699
843
  def filter_expr(expr = nil, &block)
700
844
  expr = nil if expr == []
701
845
  if expr && block
@@ -732,9 +876,8 @@ module Sequel
732
876
  # Inverts the given order by breaking it into a list of column references
733
877
  # and inverting them.
734
878
  #
735
- # dataset.invert_order([:id.desc]]) #=> [:id]
736
- # dataset.invert_order(:category, :price.desc]) #=>
737
- # [:category.desc, :price]
879
+ # DB[:items].invert_order([:id.desc]]) #=> [:id]
880
+ # DB[:items].invert_order(:category, :price.desc]) #=> [:category.desc, :price]
738
881
  def invert_order(order)
739
882
  return nil unless order
740
883
  new_order = []