sequel 4.45.0 → 4.46.0

Sign up to get free protection for your applications and to get access to all the features.
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