sequel 4.45.0 → 4.46.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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +108 -0
  3. data/doc/release_notes/4.46.0.txt +404 -0
  4. data/doc/security.rdoc +9 -0
  5. data/doc/sql.rdoc +2 -2
  6. data/doc/testing.rdoc +1 -1
  7. data/doc/validations.rdoc +1 -2
  8. data/lib/sequel/adapters/ado.rb +8 -3
  9. data/lib/sequel/adapters/ado/access.rb +8 -4
  10. data/lib/sequel/adapters/ado/mssql.rb +3 -1
  11. data/lib/sequel/adapters/amalgalite.rb +5 -0
  12. data/lib/sequel/adapters/cubrid.rb +16 -7
  13. data/lib/sequel/adapters/do.rb +7 -1
  14. data/lib/sequel/adapters/do/mysql.rb +8 -4
  15. data/lib/sequel/adapters/ibmdb.rb +10 -5
  16. data/lib/sequel/adapters/jdbc.rb +8 -2
  17. data/lib/sequel/adapters/jdbc/as400.rb +10 -3
  18. data/lib/sequel/adapters/jdbc/db2.rb +27 -16
  19. data/lib/sequel/adapters/jdbc/derby.rb +47 -20
  20. data/lib/sequel/adapters/jdbc/h2.rb +13 -7
  21. data/lib/sequel/adapters/jdbc/hsqldb.rb +18 -9
  22. data/lib/sequel/adapters/jdbc/mssql.rb +5 -2
  23. data/lib/sequel/adapters/jdbc/mysql.rb +3 -2
  24. data/lib/sequel/adapters/jdbc/oracle.rb +3 -2
  25. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -3
  26. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +2 -1
  27. data/lib/sequel/adapters/jdbc/sqlite.rb +10 -3
  28. data/lib/sequel/adapters/jdbc/sqlserver.rb +23 -0
  29. data/lib/sequel/adapters/jdbc/transactions.rb +16 -10
  30. data/lib/sequel/adapters/mock.rb +5 -0
  31. data/lib/sequel/adapters/mysql.rb +8 -1
  32. data/lib/sequel/adapters/mysql2.rb +6 -1
  33. data/lib/sequel/adapters/odbc.rb +20 -8
  34. data/lib/sequel/adapters/odbc/mssql.rb +6 -3
  35. data/lib/sequel/adapters/oracle.rb +12 -6
  36. data/lib/sequel/adapters/postgres.rb +20 -8
  37. data/lib/sequel/adapters/shared/access.rb +76 -47
  38. data/lib/sequel/adapters/shared/cubrid.rb +16 -11
  39. data/lib/sequel/adapters/shared/db2.rb +46 -19
  40. data/lib/sequel/adapters/shared/firebird.rb +20 -8
  41. data/lib/sequel/adapters/shared/informix.rb +6 -3
  42. data/lib/sequel/adapters/shared/mssql.rb +132 -72
  43. data/lib/sequel/adapters/shared/mysql.rb +112 -65
  44. data/lib/sequel/adapters/shared/oracle.rb +36 -21
  45. data/lib/sequel/adapters/shared/postgres.rb +91 -56
  46. data/lib/sequel/adapters/shared/sqlanywhere.rb +65 -37
  47. data/lib/sequel/adapters/shared/sqlite.rb +67 -32
  48. data/lib/sequel/adapters/sqlanywhere.rb +9 -1
  49. data/lib/sequel/adapters/sqlite.rb +8 -1
  50. data/lib/sequel/adapters/swift.rb +5 -0
  51. data/lib/sequel/adapters/swift/mysql.rb +4 -2
  52. data/lib/sequel/adapters/swift/sqlite.rb +1 -1
  53. data/lib/sequel/adapters/tinytds.rb +10 -3
  54. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +1 -1
  55. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +1 -1
  56. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -0
  57. data/lib/sequel/adapters/utils/pg_types.rb +14 -6
  58. data/lib/sequel/adapters/utils/replace.rb +4 -2
  59. data/lib/sequel/connection_pool/single.rb +2 -2
  60. data/lib/sequel/core.rb +24 -11
  61. data/lib/sequel/database/connecting.rb +9 -3
  62. data/lib/sequel/database/dataset_defaults.rb +7 -1
  63. data/lib/sequel/database/logging.rb +1 -0
  64. data/lib/sequel/database/misc.rb +5 -2
  65. data/lib/sequel/database/query.rb +7 -5
  66. data/lib/sequel/database/schema_generator.rb +1 -0
  67. data/lib/sequel/database/schema_methods.rb +50 -27
  68. data/lib/sequel/database/transactions.rb +19 -9
  69. data/lib/sequel/dataset/actions.rb +15 -6
  70. data/lib/sequel/dataset/graph.rb +15 -5
  71. data/lib/sequel/dataset/misc.rb +12 -4
  72. data/lib/sequel/dataset/mutation.rb +17 -8
  73. data/lib/sequel/dataset/prepared_statements.rb +3 -2
  74. data/lib/sequel/dataset/query.rb +84 -38
  75. data/lib/sequel/dataset/sql.rb +302 -191
  76. data/lib/sequel/deprecated.rb +26 -17
  77. data/lib/sequel/extensions/_deprecated_identifier_mangling.rb +2 -2
  78. data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
  79. data/lib/sequel/extensions/from_block.rb +1 -0
  80. data/lib/sequel/extensions/graph_each.rb +1 -1
  81. data/lib/sequel/extensions/identifier_mangling.rb +2 -2
  82. data/lib/sequel/extensions/migration.rb +28 -4
  83. data/lib/sequel/extensions/no_auto_literal_strings.rb +2 -0
  84. data/lib/sequel/extensions/schema_dumper.rb +4 -4
  85. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +5 -3
  86. data/lib/sequel/extensions/set_overrides.rb +2 -0
  87. data/lib/sequel/extensions/split_array_nil.rb +2 -2
  88. data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
  89. data/lib/sequel/model.rb +11 -7
  90. data/lib/sequel/model/associations.rb +5 -7
  91. data/lib/sequel/model/base.rb +47 -45
  92. data/lib/sequel/model/dataset_module.rb +9 -14
  93. data/lib/sequel/model/plugins.rb +3 -0
  94. data/lib/sequel/no_core_ext.rb +1 -0
  95. data/lib/sequel/plugins/blacklist_security.rb +1 -1
  96. data/lib/sequel/plugins/boolean_subsets.rb +7 -5
  97. data/lib/sequel/plugins/class_table_inheritance.rb +47 -10
  98. data/lib/sequel/plugins/dataset_associations.rb +1 -1
  99. data/lib/sequel/plugins/def_dataset_method.rb +90 -0
  100. data/lib/sequel/plugins/finder.rb +240 -0
  101. data/lib/sequel/plugins/inverted_subsets.rb +19 -12
  102. data/lib/sequel/plugins/many_through_many.rb +1 -1
  103. data/lib/sequel/plugins/nested_attributes.rb +1 -1
  104. data/lib/sequel/plugins/schema.rb +1 -1
  105. data/lib/sequel/plugins/single_table_inheritance.rb +7 -1
  106. data/lib/sequel/plugins/subset_conditions.rb +11 -3
  107. data/lib/sequel/plugins/whitelist_security.rb +118 -0
  108. data/lib/sequel/sql.rb +80 -36
  109. data/lib/sequel/timezones.rb +2 -0
  110. data/lib/sequel/version.rb +1 -1
  111. data/spec/adapters/mssql_spec.rb +20 -0
  112. data/spec/adapters/mysql_spec.rb +1 -1
  113. data/spec/adapters/oracle_spec.rb +12 -8
  114. data/spec/adapters/postgres_spec.rb +1 -1
  115. data/spec/adapters/spec_helper.rb +1 -1
  116. data/spec/adapters/sqlite_spec.rb +36 -34
  117. data/spec/core/connection_pool_spec.rb +2 -1
  118. data/spec/core/database_spec.rb +87 -9
  119. data/spec/core/dataset_spec.rb +501 -129
  120. data/spec/core/deprecated_spec.rb +1 -1
  121. data/spec/core/expression_filters_spec.rb +146 -60
  122. data/spec/core/mock_adapter_spec.rb +1 -1
  123. data/spec/core/object_graph_spec.rb +61 -9
  124. data/spec/core/placeholder_literalizer_spec.rb +20 -2
  125. data/spec/core/schema_generator_spec.rb +6 -6
  126. data/spec/core/schema_spec.rb +54 -5
  127. data/spec/core_extensions_spec.rb +122 -18
  128. data/spec/deprecation_helper.rb +27 -2
  129. data/spec/extensions/_deprecated_identifier_mangling_spec.rb +6 -6
  130. data/spec/extensions/association_proxies_spec.rb +2 -2
  131. data/spec/extensions/auto_literal_strings_spec.rb +212 -0
  132. data/spec/extensions/blacklist_security_spec.rb +1 -0
  133. data/spec/extensions/class_table_inheritance_spec.rb +1037 -39
  134. data/spec/extensions/column_select_spec.rb +20 -8
  135. data/spec/extensions/columns_introspection_spec.rb +3 -3
  136. data/spec/extensions/core_refinements_spec.rb +29 -12
  137. data/spec/extensions/dataset_associations_spec.rb +12 -12
  138. data/spec/extensions/def_dataset_method_spec.rb +100 -0
  139. data/spec/extensions/error_sql_spec.rb +1 -1
  140. data/spec/extensions/finder_spec.rb +260 -0
  141. data/spec/extensions/graph_each_spec.rb +2 -2
  142. data/spec/extensions/identifier_mangling_spec.rb +14 -8
  143. data/spec/extensions/inverted_subsets_spec.rb +4 -4
  144. data/spec/extensions/lazy_attributes_spec.rb +7 -0
  145. data/spec/extensions/many_through_many_spec.rb +38 -14
  146. data/spec/extensions/nested_attributes_spec.rb +18 -6
  147. data/spec/extensions/no_auto_literal_strings_spec.rb +1 -1
  148. data/spec/extensions/pg_enum_spec.rb +16 -1
  149. data/spec/extensions/pg_interval_spec.rb +11 -2
  150. data/spec/extensions/pg_loose_count_spec.rb +5 -0
  151. data/spec/extensions/pg_row_spec.rb +25 -0
  152. data/spec/extensions/prepared_statements_spec.rb +10 -1
  153. data/spec/extensions/query_spec.rb +2 -2
  154. data/spec/extensions/schema_dumper_spec.rb +2 -2
  155. data/spec/extensions/schema_spec.rb +2 -2
  156. data/spec/extensions/set_overrides_spec.rb +7 -3
  157. data/spec/extensions/sql_expr_spec.rb +0 -1
  158. data/spec/extensions/subset_conditions_spec.rb +6 -6
  159. data/spec/extensions/table_select_spec.rb +24 -12
  160. data/spec/extensions/to_dot_spec.rb +4 -4
  161. data/spec/extensions/whitelist_security_spec.rb +131 -0
  162. data/spec/integration/dataset_test.rb +9 -5
  163. data/spec/integration/model_test.rb +2 -0
  164. data/spec/integration/plugin_test.rb +2 -2
  165. data/spec/integration/spec_helper.rb +1 -1
  166. data/spec/model/associations_spec.rb +39 -11
  167. data/spec/model/base_spec.rb +44 -24
  168. data/spec/model/class_dataset_methods_spec.rb +18 -16
  169. data/spec/model/dataset_methods_spec.rb +4 -4
  170. data/spec/model/eager_loading_spec.rb +84 -24
  171. data/spec/model/model_spec.rb +97 -63
  172. data/spec/model/record_spec.rb +21 -13
  173. metadata +13 -2
@@ -27,22 +27,29 @@ module Sequel
27
27
  # # SELECT * FROM albums WHERE (published IS NOT TRUE)
28
28
  #
29
29
  module InvertedSubsets
30
- # Default naming for inverted subsets
31
- DEFAULT_NAME_BLOCK = lambda{|name| "not_#{name}"}
32
-
33
- # Store the supplied block for calling later when subsets are defined, or
34
- # create a default one if we need to.
35
- def self.configure(model, &block)
36
- model.instance_variable_set(:@inverted_subsets_name_block, block || DEFAULT_NAME_BLOCK)
30
+ def self.apply(mod, &block)
31
+ mod.instance_exec do
32
+ @dataset_module_class = Class.new(@dataset_module_class) do
33
+ include DatasetModuleMethods
34
+ if block
35
+ define_method(:inverted_subset_name, &block)
36
+ private :inverted_subset_name
37
+ end
38
+ end
39
+ end
37
40
  end
38
41
 
39
- module ClassMethods
40
- Plugins.inherited_instance_variables(self, :@inverted_subsets_name_block => nil)
41
-
42
+ module DatasetModuleMethods
42
43
  # Define a not_ prefixed subset which inverts the subset condition.
43
- def subset(name, *args, &block)
44
+ def where(name, *args, &block)
44
45
  super
45
- def_dataset_method(@inverted_subsets_name_block.call(name)){exclude(*args, &block)}
46
+ exclude(inverted_subset_name(name), *args, &block)
47
+ end
48
+
49
+ private
50
+
51
+ def inverted_subset_name(name)
52
+ "not_#{name}"
46
53
  end
47
54
  end
48
55
  end
@@ -283,7 +283,7 @@ module Sequel
283
283
  iq = nil
284
284
  end
285
285
  fe = opts.final_edge
286
- ds.graph(opts.associated_class, use_only_conditions ? only_conditions : (Array(opts.right_primary_key).zip(Array(fe[:left])) + conditions), :select=>select, :table_alias=>eo[:table_alias], :qualify=>:deep, :join_type=>eo[:join_type]||join_type, :join_only=>eo[:join_only], &graph_block)
286
+ ds.graph(opts.associated_class.dataset, use_only_conditions ? only_conditions : (Array(opts.right_primary_key).zip(Array(fe[:left])) + conditions), :select=>select, :table_alias=>eo[:table_alias], :qualify=>:deep, :join_type=>eo[:join_type]||join_type, :join_only=>eo[:join_only], &graph_block)
287
287
  end
288
288
  end
289
289
  end
@@ -245,7 +245,7 @@ module Sequel
245
245
  def nested_attributes_set_attributes(meta, obj, attributes)
246
246
  if fields = meta[:fields]
247
247
  fields = fields.call(obj) if fields.respond_to?(:call)
248
- obj.set_only(attributes, fields)
248
+ obj.set_fields(attributes, fields, :missing=>:skip)
249
249
  else
250
250
  obj.set(attributes)
251
251
  end
@@ -59,7 +59,7 @@ module Sequel
59
59
  @schema || (superclass.schema unless superclass == Model)
60
60
  end
61
61
 
62
- # Defines a table schema (see Schema::Generator for more information).
62
+ # Defines a table schema (see Schema::CreateTableGenerator for more information).
63
63
  #
64
64
  # This is only needed if you want to use the create_table/create_table! methods.
65
65
  # Will also set the dataset if you provide a name, as well as setting
@@ -171,7 +171,7 @@ module Sequel
171
171
  key = Array(sti_key_map[subclass]).dup
172
172
  sti_subclass_added(key)
173
173
  rp = dataset.row_proc
174
- subclass.set_dataset(sti_dataset.where(SQL::QualifiedIdentifier.new(sti_dataset.first_source_alias, sti_key)=>Sequel.delay{Sequel.synchronize{key}}), :inherited=>true)
174
+ subclass.set_dataset(sti_subclass_dataset(key), :inherited=>true)
175
175
  subclass.instance_eval do
176
176
  @dataset = @dataset.with_row_proc(rp)
177
177
  @sti_key_array = key
@@ -230,6 +230,12 @@ module Sequel
230
230
  raise(Error, "Invalid class type used: #{v.inspect}")
231
231
  end
232
232
  end
233
+
234
+ # Use the given dataset for the subclass, with key being the allowed
235
+ # values for the sti_kind field.
236
+ def sti_subclass_dataset(key)
237
+ sti_dataset.where(SQL::QualifiedIdentifier.new(sti_dataset.first_source_alias, sti_key)=>Sequel.delay{Sequel.synchronize{key}})
238
+ end
233
239
  end
234
240
 
235
241
  module InstanceMethods
@@ -24,13 +24,21 @@ module Sequel
24
24
  # Album.where(Sequel.|(Album.published_conditions, :ready=>true)).sql
25
25
  # # SELECT * FROM albums WHERE ((published IS TRUE) OR (ready IS TRUE))
26
26
  module SubsetConditions
27
- module ClassMethods
27
+ def self.apply(mod, &block)
28
+ mod.instance_exec do
29
+ @dataset_module_class = Class.new(@dataset_module_class) do
30
+ include DatasetModuleMethods
31
+ end
32
+ end
33
+ end
34
+
35
+ module DatasetModuleMethods
28
36
  # Also create a method that returns the conditions the filter uses.
29
- def subset(name, *args, &block)
37
+ def where(name, *args, &block)
30
38
  super
31
39
  cond = args
32
40
  cond = cond.first if cond.size == 1
33
- def_dataset_method(:"#{name}_conditions"){filter_expr(cond, &block)}
41
+ define_method(:"#{name}_conditions"){filter_expr(cond, &block)}
34
42
  end
35
43
  end
36
44
  end
@@ -0,0 +1,118 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The whitelist_security plugin contains whitelist-based support for
6
+ # mass assignment, explicitly specifying which columns to allow mass assignment for,
7
+ # disallowing mass assignment for columns not listed. This exists mostly for backwards
8
+ # compatibility, it's best to use Sequel::Model#set_fields and Sequel::Model#update_fields
9
+ # to decide which fields to allow on a per-call basis.
10
+ #
11
+ # Usage:
12
+ #
13
+ # # Make all model subclasses support allowed_columns
14
+ # Sequel::Model.plugin :whitelist_security
15
+ #
16
+ # # Make the Album class support allowed_columns
17
+ # Album.plugin :whitelist_security
18
+ module WhitelistSecurity
19
+ module ClassMethods
20
+ # Which columns should be the only columns allowed in a call to a mass assignment method (e.g. set)
21
+ # (default: not set, so all columns not otherwise restricted are allowed).
22
+ attr_reader :allowed_columns
23
+
24
+ Plugins.inherited_instance_variables(self, :@allowed_columns=>:dup)
25
+
26
+ # Freeze allowed columns when freezing model class.
27
+ def freeze
28
+ @allowed_columns.freeze
29
+ super
30
+ end
31
+
32
+ # Set the columns to allow when using mass assignment (e.g. +set+). Using this means that
33
+ # any columns not listed here will not be modified. If you have any virtual
34
+ # setter methods (methods that end in =) that you want to be used during
35
+ # mass assignment, they need to be listed here as well (without the =).
36
+ #
37
+ # It may be better to use a method such as +set_only+ or +set_fields+ that lets you specify
38
+ # the allowed fields per call.
39
+ #
40
+ # Artist.set_allowed_columns(:name, :hometown)
41
+ # Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
42
+ # Artist.set(:name=>'Bob', :records_sold=>30000) # Error
43
+ def set_allowed_columns(*cols)
44
+ clear_setter_methods_cache
45
+ @allowed_columns = cols
46
+ end
47
+
48
+ private
49
+
50
+ # If allowed_columns is set, only allow those columns.
51
+ def get_setter_methods
52
+ if allowed_columns
53
+ allowed_columns.map{|x| "#{x}="}
54
+ else
55
+ super
56
+ end
57
+ end
58
+ end
59
+
60
+ module InstanceMethods
61
+ # Set all values using the entries in the hash, ignoring any setting of
62
+ # allowed_columns in the model.
63
+ #
64
+ # Artist.set_allowed_columns(:num_albums)
65
+ # artist.set_all(:name=>'Jim')
66
+ # artist.name # => 'Jim'
67
+ def set_all(hash)
68
+ set_restricted(hash, :all)
69
+ end
70
+
71
+ # Set the values using the entries in the hash, only if the key
72
+ # is included in only. It may be a better idea to use +set_fields+
73
+ # instead of this method.
74
+ #
75
+ # artist.set_only({:name=>'Jim'}, :name)
76
+ # artist.name # => 'Jim'
77
+ #
78
+ # artist.set_only({:hometown=>'LA'}, :name) # Raise Error
79
+ def set_only(hash, *only)
80
+ set_restricted(hash, only.flatten)
81
+ end
82
+
83
+ # Update all values using the entries in the hash, ignoring any setting of
84
+ # +allowed_columns+ in the model.
85
+ #
86
+ # Artist.set_allowed_columns(:num_albums)
87
+ # artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
88
+ def update_all(hash)
89
+ update_restricted(hash, :all)
90
+ end
91
+
92
+ # Update the values using the entries in the hash, only if the key
93
+ # is included in only. It may be a better idea to use +update_fields+
94
+ # instead of this method.
95
+ #
96
+ # artist.update_only({:name=>'Jim'}, :name)
97
+ # # UPDATE artists SET name = 'Jim' WHERE (id = 1)
98
+ #
99
+ # artist.update_only({:hometown=>'LA'}, :name) # Raise Error
100
+ def update_only(hash, *only)
101
+ update_restricted(hash, only.flatten)
102
+ end
103
+
104
+ private
105
+
106
+ # If allowed_columns is set and set/update is called, only allow those columns.
107
+ def setter_methods(type)
108
+ if type == :default && model.allowed_columns
109
+ model.setter_methods
110
+ else
111
+ super
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+
@@ -35,6 +35,7 @@ module Sequel
35
35
 
36
36
  # No-op method on ruby 1.9, which has a real +BasicObject+ class.
37
37
  def self.remove_methods!
38
+ Sequel::Deprecation.deprecate("Sequel::BasicObject#remove_methods!", "It has no effect, so stop calling it")
38
39
  end
39
40
  end
40
41
  end
@@ -58,7 +59,8 @@ module Sequel
58
59
  # Create a new SQLTime instance given an hour, minute, and second.
59
60
  def create(hour, minute, second, usec = 0)
60
61
  t = date
61
- local(t.year, t.month, t.day, hour, minute, second, usec)
62
+ meth = Sequel.application_timezone == :utc ? :utc : :local
63
+ send(meth, t.year, t.month, t.day, hour, minute, second, usec)
62
64
  end
63
65
  end
64
66
 
@@ -142,19 +144,20 @@ module Sequel
142
144
  ([self.class] + self.class.comparison_attrs.map{|x| send(x)}).hash
143
145
  end
144
146
 
145
- # Show the class name and instance variables for the object, necessary
146
- # for correct operation on ruby 1.9.2.
147
+ # Show the class name and instance variables for the object.
147
148
  def inspect
148
149
  "#<#{self.class} #{instance_variables.map{|iv| "#{iv}=>#{instance_variable_get(iv).inspect}"}.join(', ')}>"
149
150
  end
150
151
 
151
152
  # Returns +self+, because <tt>SQL::Expression</tt> already acts like +LiteralString+.
152
153
  def lit
154
+ Sequel::Deprecation.deprecate("Sequel::SQL::Expression#lit", "This method returns self, so just use the receiver")
153
155
  self
154
156
  end
155
157
 
156
158
  # Alias of +to_s+
157
159
  def sql_literal(ds)
160
+ Sequel::Deprecation.deprecate("Sequel::SQL::Expression#sql_literal", "Call Sequel::Dataset#literal with the expression instead")
158
161
  s = String.new
159
162
  to_s_append(ds, s)
160
163
  s
@@ -379,7 +382,7 @@ module Sequel
379
382
  #
380
383
  # Sequel.case([[{:a=>[2,3]}, 1]], 0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
381
384
  # Sequel.case({:a=>1}, 0, :b) # SQL: CASE b WHEN a THEN 1 ELSE 0 END
382
- def case(*args) # core_sql ignore
385
+ def case(*args)
383
386
  SQL::CaseExpression.new(*args)
384
387
  end
385
388
 
@@ -612,7 +615,7 @@ module Sequel
612
615
  #
613
616
  # DB[:items].select{|o| o.count(Sequel.lit('DISTINCT ?', :a))}.sql #=>
614
617
  # "SELECT count(DISTINCT a) FROM items"
615
- def lit(s, *args) # core_sql ignore
618
+ def lit(s, *args)
616
619
  if args.empty?
617
620
  if s.is_a?(LiteralString)
618
621
  s
@@ -1108,7 +1111,7 @@ module Sequel
1108
1111
  def self.from_value_pairs(pairs, op=:AND, negate=false)
1109
1112
  pairs = pairs.map{|l,r| from_value_pair(l, r)}
1110
1113
  pairs.map!{|ce| invert(ce)} if negate
1111
- pairs.length == 1 ? pairs.at(0) : new(op, *pairs)
1114
+ pairs.length == 1 ? pairs[0] : new(op, *pairs)
1112
1115
  end
1113
1116
 
1114
1117
  # Return a BooleanExpression based on the right side of the pair.
@@ -1422,6 +1425,7 @@ module Sequel
1422
1425
  end
1423
1426
 
1424
1427
  # Return a new function with an OVER clause (making it a window function).
1428
+ # See {SQL::Window} for the list of options +over+ can receive.
1425
1429
  #
1426
1430
  # Sequel.function(:row_number).over(:partition=>:col) # row_number() OVER (PARTITION BY col)
1427
1431
  def over(window=OPTS)
@@ -1614,8 +1618,14 @@ module Sequel
1614
1618
  # Create an object with the given string, placeholder arguments, and parens flag.
1615
1619
  def initialize(str, args, parens=false)
1616
1620
  @str = str
1617
- @args = args.is_a?(Array) && args.length == 1 && (v = args.at(0)).is_a?(Hash) ? v : args
1621
+ @args = args.is_a?(Array) && args.length == 1 && (v = args[0]).is_a?(Hash) ? v : args
1618
1622
  @parens = parens
1623
+ freeze
1624
+ end
1625
+
1626
+ # Return a copy of the that will be surrounded by parantheses.
1627
+ def with_parens
1628
+ @parens ? self : self.class.new(@str, @args, true)
1619
1629
  end
1620
1630
 
1621
1631
  to_s_method :placeholder_literal_string_sql
@@ -1758,7 +1768,7 @@ module Sequel
1758
1768
  r, rre, rci = like_element(ce)
1759
1769
  BooleanExpression.new(LIKE_MAP[[lre||rre, lci||rci]], l, r)
1760
1770
  end
1761
- ces.length == 1 ? ces.at(0) : BooleanExpression.new(:OR, *ces)
1771
+ ces.length == 1 ? ces[0] : BooleanExpression.new(:OR, *ces)
1762
1772
  end
1763
1773
 
1764
1774
  # Returns a three element array, made up of:
@@ -1925,47 +1935,81 @@ module Sequel
1925
1935
 
1926
1936
  # Return a literal string created with the given string.
1927
1937
  def `(s)
1938
+ Sequel::Deprecation.deprecate("Using Sequel#VirtualRow#` to create a literal SQL fragment", "Use Sequel.lit instead")
1928
1939
  Sequel::LiteralString.new(s)
1929
1940
  end
1930
1941
 
1931
- # Return an +Identifier+, +QualifiedIdentifier+, or +Function+, depending
1932
- # on arguments and whether a block is provided. Does not currently call the block.
1933
- # See the class level documentation.
1934
- def method_missing(m, *args, &block)
1935
- if block
1936
- if args.empty?
1937
- Function.new(m)
1938
- else
1939
- case args.shift
1940
- when :*
1941
- Function.new(m, *args).*
1942
- when :distinct
1943
- Function.new(m, *args).distinct
1944
- when :over
1945
- opts = args.shift || OPTS
1946
- f = Function.new(m, *::Kernel.Array(opts[:args]))
1947
- f = f.* if opts[:*]
1948
- f.over(opts)
1942
+ include(Module.new do
1943
+ # Return an +Identifier+, +QualifiedIdentifier+, or +Function+, depending
1944
+ # on arguments and whether a block is provided. Does not currently call the block.
1945
+ # See the class level documentation.
1946
+ def method_missing(m, *args, &block)
1947
+ if block
1948
+ if args.empty?
1949
+ Sequel::Deprecation.deprecate("Passing a block to a virtual row method to create a Sequel::SQL::Function", "Replace the block with a call to .function to create a function, or use the virtual_row_method_block extension")
1950
+ Function.new(m)
1949
1951
  else
1950
- Kernel.raise(Error, 'unsupported VirtualRow method argument used with block')
1952
+ case args.shift
1953
+ when :*
1954
+ Sequel::Deprecation.deprecate("Passing a block to a virtual row method with a :* argument to create a Sequel::SQL::Function", "Remove the :* argument and block and a call to .function.* to create a function(*) call, or use the virtual_row_method_block extension")
1955
+ Function.new(m, *args).*
1956
+ when :distinct
1957
+ Sequel::Deprecation.deprecate("Passing a block to a virtual row method with a :distinct argument to create a Sequel::SQL::Function", "Remove the :distinct argument and block with a call to .function.distinct to create a function(DISTINCT ...) call, or use the virtual_row_method_block extension")
1958
+ Function.new(m, *args).distinct
1959
+ when :over
1960
+ opts = args.shift || OPTS
1961
+ f = Function.new(m, *::Kernel.Array(opts[:args]))
1962
+ if opts[:*]
1963
+ Sequel::Deprecation.deprecate("Passing a block to a virtual row method with a :over argument and :* option to create a Sequel::SQL::WindowFunction", "Remove the :over argument, :* option and block with a call to .function.*.over with the options to create a function(*) OVER (...) call, or use the virtual_row_method_block extension")
1964
+ f = f.*
1965
+ else
1966
+ Sequel::Deprecation.deprecate("Passing a block to a virtual row method with a :over argument to create a Sequel::SQL::WindowFunction", "Remove the :over argument and block with a call to .function.over with the options to create a function(...) OVER (...) call, or use the virtual_row_method_block extension")
1967
+ end
1968
+ f.over(opts)
1969
+ else
1970
+ Kernel.raise(Error, 'unsupported VirtualRow method argument used with block')
1971
+ end
1972
+ end
1973
+ elsif args.empty?
1974
+ if split = Sequel.split_symbols?
1975
+ table, column = m.to_s.split(DOUBLE_UNDERSCORE, 2)
1976
+ if column && split == :deprecated
1977
+ Sequel::Deprecation.deprecate("Splitting virtual row method names", "Either set Sequel.split_symbols = true, or change #{m.inspect} to #{table}[:#{column}]")
1978
+ end
1979
+ column ? QualifiedIdentifier.new(table, column) : Identifier.new(m)
1980
+ else
1981
+ Identifier.new(m)
1951
1982
  end
1952
- end
1953
- elsif args.empty?
1954
- if Sequel.split_symbols?
1955
- table, column = m.to_s.split(DOUBLE_UNDERSCORE, 2)
1956
- column ? QualifiedIdentifier.new(table, column) : Identifier.new(m)
1957
1983
  else
1958
- Identifier.new(m)
1984
+ Function.new(m, *args)
1959
1985
  end
1960
- else
1961
- Function.new(m, *args)
1962
1986
  end
1963
- end
1987
+ end)
1964
1988
 
1965
1989
  Sequel::VIRTUAL_ROW = new
1966
1990
  end
1967
1991
 
1968
1992
  # A +Window+ is part of a window function specifying the window over which a window function operates.
1993
+ #
1994
+ # Sequel::SQL::Window.new(partition: :col1)
1995
+ # # (PARTITION BY col1)
1996
+ # Sequel::SQL::Window.new(partition: [:col2, :col3])
1997
+ # # (PARTITION BY col2, col3)
1998
+ #
1999
+ # Sequel::SQL::Window.new(order: :col4)
2000
+ # # (ORDER BY col4)
2001
+ # Sequel::SQL::Window.new(order: [:col5, Sequel.desc(:col6)])
2002
+ # # (ORDER BY col5, col6 DESC)
2003
+ #
2004
+ # Sequel::SQL::Window.new(partition: :col7, frame: :all)
2005
+ # # (PARTITION BY col7 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
2006
+ # Sequel::SQL::Window.new(partition: :col7, frame: :rows)
2007
+ # # (PARTITION BY col7 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
2008
+ # Sequel::SQL::Window.new(partition: :col7, frame: "RANGE CURRENT ROW")
2009
+ # # (PARTITION BY col7 RANGE CURRENT ROW)
2010
+ #
2011
+ # Sequel::SQL::Window.new(window: :named_window) # you can create a named window with Dataset#window
2012
+ # # (named_window)
1969
2013
  class Window < Expression
1970
2014
  # The options for this window. Options currently supported:
1971
2015
  # :frame :: if specified, should be :all, :rows, or a String that is used literally. :all always operates over all rows in the
@@ -171,6 +171,7 @@ module Sequel
171
171
  v.to_datetime
172
172
  else
173
173
  # :nocov:
174
+ # SEQUEL5: Remove
174
175
  # Ruby 1.8 code, %N not available and %z broken on Windows
175
176
  offset_hours, offset_minutes = (v.utc_offset/60).divmod(60)
176
177
  string_to_datetime(v.strftime("%Y-%m-%dT%H:%M:%S") << sprintf(".%06i%+03i%02i", v.usec, offset_hours, offset_minutes))
@@ -186,6 +187,7 @@ module Sequel
186
187
  v.to_time
187
188
  else
188
189
  # :nocov:
190
+ # SEQUEL5: Remove
189
191
  string_to_datetime(v.strftime("%FT%T.%N%z"))
190
192
  # :nocov:
191
193
  end