sequel 3.13.0 → 3.14.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.
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 = []