sequel 4.41.0 → 4.42.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 (256) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +98 -0
  3. data/README.rdoc +23 -10
  4. data/doc/active_record.rdoc +4 -4
  5. data/doc/advanced_associations.rdoc +2 -2
  6. data/doc/association_basics.rdoc +5 -2
  7. data/doc/cheat_sheet.rdoc +3 -3
  8. data/doc/core_extensions.rdoc +2 -2
  9. data/doc/dataset_basics.rdoc +4 -4
  10. data/doc/dataset_filtering.rdoc +1 -1
  11. data/doc/migration.rdoc +19 -1
  12. data/doc/prepared_statements.rdoc +2 -2
  13. data/doc/release_notes/4.42.0.txt +221 -0
  14. data/doc/testing.rdoc +3 -1
  15. data/lib/sequel/adapters/ado/access.rb +0 -1
  16. data/lib/sequel/adapters/ado/mssql.rb +0 -1
  17. data/lib/sequel/adapters/do/mysql.rb +0 -1
  18. data/lib/sequel/adapters/do/postgres.rb +0 -1
  19. data/lib/sequel/adapters/do/sqlite3.rb +0 -1
  20. data/lib/sequel/adapters/ibmdb.rb +21 -25
  21. data/lib/sequel/adapters/jdbc.rb +8 -16
  22. data/lib/sequel/adapters/jdbc/as400.rb +0 -1
  23. data/lib/sequel/adapters/jdbc/cubrid.rb +0 -1
  24. data/lib/sequel/adapters/jdbc/db2.rb +0 -1
  25. data/lib/sequel/adapters/jdbc/derby.rb +0 -1
  26. data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -1
  27. data/lib/sequel/adapters/jdbc/h2.rb +0 -1
  28. data/lib/sequel/adapters/jdbc/hsqldb.rb +0 -1
  29. data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -1
  30. data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -1
  31. data/lib/sequel/adapters/jdbc/jtds.rb +0 -1
  32. data/lib/sequel/adapters/jdbc/mssql.rb +0 -1
  33. data/lib/sequel/adapters/jdbc/mysql.rb +0 -1
  34. data/lib/sequel/adapters/jdbc/oracle.rb +0 -1
  35. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -13
  36. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +0 -1
  37. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -1
  38. data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -4
  39. data/lib/sequel/adapters/mock.rb +54 -12
  40. data/lib/sequel/adapters/mysql.rb +1 -1
  41. data/lib/sequel/adapters/mysql2.rb +11 -17
  42. data/lib/sequel/adapters/odbc/mssql.rb +0 -1
  43. data/lib/sequel/adapters/oracle.rb +8 -20
  44. data/lib/sequel/adapters/postgres.rb +11 -29
  45. data/lib/sequel/adapters/shared/access.rb +5 -12
  46. data/lib/sequel/adapters/shared/cubrid.rb +4 -13
  47. data/lib/sequel/adapters/shared/db2.rb +4 -2
  48. data/lib/sequel/adapters/shared/firebird.rb +2 -4
  49. data/lib/sequel/adapters/shared/informix.rb +4 -2
  50. data/lib/sequel/adapters/shared/mssql.rb +3 -5
  51. data/lib/sequel/adapters/shared/mysql.rb +4 -14
  52. data/lib/sequel/adapters/shared/oracle.rb +1 -3
  53. data/lib/sequel/adapters/shared/postgres.rb +16 -38
  54. data/lib/sequel/adapters/shared/progress.rb +0 -2
  55. data/lib/sequel/adapters/shared/sqlanywhere.rb +0 -2
  56. data/lib/sequel/adapters/shared/sqlite.rb +20 -16
  57. data/lib/sequel/adapters/sqlite.rb +8 -20
  58. data/lib/sequel/adapters/swift/mysql.rb +0 -1
  59. data/lib/sequel/adapters/swift/postgres.rb +0 -1
  60. data/lib/sequel/adapters/swift/sqlite.rb +0 -1
  61. data/lib/sequel/adapters/tinytds.rb +4 -12
  62. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +1 -1
  63. data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -2
  64. data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +11 -34
  65. data/lib/sequel/adapters/utils/stored_procedures.rb +9 -22
  66. data/lib/sequel/adapters/utils/unmodified_identifiers.rb +26 -0
  67. data/lib/sequel/ast_transformer.rb +2 -2
  68. data/lib/sequel/database/dataset.rb +1 -1
  69. data/lib/sequel/database/dataset_defaults.rb +0 -66
  70. data/lib/sequel/database/features.rb +6 -0
  71. data/lib/sequel/database/misc.rb +31 -17
  72. data/lib/sequel/database/query.rb +7 -4
  73. data/lib/sequel/database/schema_methods.rb +1 -1
  74. data/lib/sequel/dataset.rb +8 -8
  75. data/lib/sequel/dataset/actions.rb +140 -46
  76. data/lib/sequel/dataset/features.rb +1 -5
  77. data/lib/sequel/dataset/graph.rb +7 -8
  78. data/lib/sequel/dataset/misc.rb +127 -56
  79. data/lib/sequel/dataset/mutation.rb +9 -20
  80. data/lib/sequel/dataset/placeholder_literalizer.rb +10 -1
  81. data/lib/sequel/dataset/prepared_statements.rb +102 -46
  82. data/lib/sequel/dataset/query.rb +155 -72
  83. data/lib/sequel/dataset/sql.rb +26 -9
  84. data/lib/sequel/extensions/columns_introspection.rb +3 -1
  85. data/lib/sequel/extensions/core_extensions.rb +5 -5
  86. data/lib/sequel/extensions/core_refinements.rb +5 -5
  87. data/lib/sequel/extensions/duplicate_columns_handler.rb +4 -2
  88. data/lib/sequel/extensions/freeze_datasets.rb +69 -0
  89. data/lib/sequel/extensions/identifier_mangling.rb +196 -0
  90. data/lib/sequel/extensions/looser_typecasting.rb +11 -7
  91. data/lib/sequel/extensions/migration.rb +1 -1
  92. data/lib/sequel/extensions/null_dataset.rb +5 -2
  93. data/lib/sequel/extensions/pagination.rb +42 -23
  94. data/lib/sequel/extensions/pg_enum.rb +3 -3
  95. data/lib/sequel/extensions/query.rb +3 -3
  96. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +15 -8
  97. data/lib/sequel/model/associations.rb +25 -8
  98. data/lib/sequel/model/base.rb +88 -29
  99. data/lib/sequel/model/dataset_module.rb +37 -0
  100. data/lib/sequel/plugins/association_pks.rb +4 -4
  101. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  102. data/lib/sequel/plugins/constraint_validations.rb +1 -2
  103. data/lib/sequel/plugins/csv_serializer.rb +2 -2
  104. data/lib/sequel/plugins/dataset_associations.rb +8 -8
  105. data/lib/sequel/plugins/eager_each.rb +2 -2
  106. data/lib/sequel/plugins/instance_filters.rb +1 -1
  107. data/lib/sequel/plugins/json_serializer.rb +2 -2
  108. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  109. data/lib/sequel/plugins/list.rb +4 -4
  110. data/lib/sequel/plugins/prepared_statements.rb +2 -4
  111. data/lib/sequel/plugins/prepared_statements_associations.rb +1 -3
  112. data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
  113. data/lib/sequel/plugins/rcte_tree.rb +13 -13
  114. data/lib/sequel/plugins/sharding.rb +1 -1
  115. data/lib/sequel/plugins/single_table_inheritance.rb +9 -4
  116. data/lib/sequel/plugins/tactical_eager_loading.rb +4 -4
  117. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  118. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  119. data/lib/sequel/plugins/xml_serializer.rb +2 -2
  120. data/lib/sequel/sql.rb +69 -36
  121. data/lib/sequel/version.rb +1 -1
  122. data/spec/adapters/db2_spec.rb +10 -0
  123. data/spec/adapters/firebird_spec.rb +1 -1
  124. data/spec/adapters/mssql_spec.rb +4 -5
  125. data/spec/adapters/mysql_spec.rb +9 -9
  126. data/spec/adapters/postgres_spec.rb +67 -68
  127. data/spec/adapters/spec_helper.rb +6 -1
  128. data/spec/adapters/sqlite_spec.rb +29 -15
  129. data/spec/core/connection_pool_spec.rb +14 -14
  130. data/spec/core/database_spec.rb +38 -180
  131. data/spec/core/dataset_mutation_spec.rb +253 -0
  132. data/spec/core/dataset_spec.rb +394 -537
  133. data/spec/core/expression_filters_spec.rb +34 -32
  134. data/spec/core/mock_adapter_spec.rb +27 -35
  135. data/spec/core/placeholder_literalizer_spec.rb +2 -4
  136. data/spec/core/schema_generator_spec.rb +4 -4
  137. data/spec/core/schema_spec.rb +1 -2
  138. data/spec/core_extensions_spec.rb +22 -29
  139. data/spec/extensions/active_model_spec.rb +6 -6
  140. data/spec/extensions/association_dependencies_spec.rb +2 -2
  141. data/spec/extensions/blacklist_security_spec.rb +3 -3
  142. data/spec/extensions/boolean_readers_spec.rb +12 -12
  143. data/spec/extensions/caching_spec.rb +13 -10
  144. data/spec/extensions/class_table_inheritance_spec.rb +38 -43
  145. data/spec/extensions/column_conflicts_spec.rb +1 -3
  146. data/spec/extensions/columns_introspection_spec.rb +2 -3
  147. data/spec/extensions/composition_spec.rb +5 -3
  148. data/spec/extensions/constraint_validations_plugin_spec.rb +5 -5
  149. data/spec/extensions/constraint_validations_spec.rb +14 -8
  150. data/spec/extensions/core_refinements_spec.rb +22 -29
  151. data/spec/extensions/csv_serializer_spec.rb +7 -6
  152. data/spec/extensions/date_arithmetic_spec.rb +15 -15
  153. data/spec/extensions/defaults_setter_spec.rb +2 -2
  154. data/spec/extensions/delay_add_association_spec.rb +1 -1
  155. data/spec/extensions/dirty_spec.rb +19 -10
  156. data/spec/extensions/duplicate_columns_handler_spec.rb +12 -18
  157. data/spec/extensions/eager_each_spec.rb +12 -16
  158. data/spec/extensions/empty_array_consider_nulls_spec.rb +1 -1
  159. data/spec/extensions/eval_inspect_spec.rb +4 -3
  160. data/spec/extensions/force_encoding_spec.rb +12 -12
  161. data/spec/extensions/freeze_datasets_spec.rb +31 -0
  162. data/spec/extensions/graph_each_spec.rb +6 -18
  163. data/spec/extensions/hook_class_methods_spec.rb +7 -7
  164. data/spec/extensions/identifier_mangling_spec.rb +307 -0
  165. data/spec/extensions/instance_filters_spec.rb +5 -6
  166. data/spec/extensions/instance_hooks_spec.rb +12 -12
  167. data/spec/extensions/json_serializer_spec.rb +12 -15
  168. data/spec/extensions/lazy_attributes_spec.rb +4 -4
  169. data/spec/extensions/list_spec.rb +19 -21
  170. data/spec/extensions/many_through_many_spec.rb +108 -163
  171. data/spec/extensions/meta_def_spec.rb +7 -2
  172. data/spec/extensions/migration_spec.rb +10 -12
  173. data/spec/extensions/mssql_optimistic_locking_spec.rb +4 -3
  174. data/spec/extensions/named_timezones_spec.rb +4 -3
  175. data/spec/extensions/nested_attributes_spec.rb +2 -2
  176. data/spec/extensions/null_dataset_spec.rb +17 -12
  177. data/spec/extensions/optimistic_locking_spec.rb +4 -5
  178. data/spec/extensions/pagination_spec.rb +8 -10
  179. data/spec/extensions/pg_array_associations_spec.rb +28 -27
  180. data/spec/extensions/pg_array_ops_spec.rb +2 -1
  181. data/spec/extensions/pg_array_spec.rb +6 -2
  182. data/spec/extensions/pg_enum_spec.rb +5 -3
  183. data/spec/extensions/pg_hstore_ops_spec.rb +3 -1
  184. data/spec/extensions/pg_hstore_spec.rb +7 -6
  185. data/spec/extensions/pg_inet_ops_spec.rb +2 -1
  186. data/spec/extensions/pg_inet_spec.rb +2 -1
  187. data/spec/extensions/pg_interval_spec.rb +2 -1
  188. data/spec/extensions/pg_json_ops_spec.rb +2 -1
  189. data/spec/extensions/pg_json_spec.rb +6 -3
  190. data/spec/extensions/pg_loose_count_spec.rb +1 -0
  191. data/spec/extensions/pg_range_ops_spec.rb +3 -1
  192. data/spec/extensions/pg_range_spec.rb +9 -5
  193. data/spec/extensions/pg_row_ops_spec.rb +2 -1
  194. data/spec/extensions/pg_row_plugin_spec.rb +4 -6
  195. data/spec/extensions/pg_row_spec.rb +5 -3
  196. data/spec/extensions/pg_static_cache_updater_spec.rb +2 -1
  197. data/spec/extensions/pg_typecast_on_load_spec.rb +1 -1
  198. data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
  199. data/spec/extensions/prepared_statements_spec.rb +12 -11
  200. data/spec/extensions/pretty_table_spec.rb +1 -1
  201. data/spec/extensions/query_spec.rb +8 -5
  202. data/spec/extensions/rcte_tree_spec.rb +39 -39
  203. data/spec/extensions/round_timestamps_spec.rb +2 -2
  204. data/spec/extensions/schema_dumper_spec.rb +3 -2
  205. data/spec/extensions/schema_spec.rb +2 -2
  206. data/spec/extensions/scissors_spec.rb +1 -2
  207. data/spec/extensions/sequel_3_dataset_methods_spec.rb +30 -17
  208. data/spec/extensions/serialization_modification_detection_spec.rb +2 -2
  209. data/spec/extensions/serialization_spec.rb +15 -13
  210. data/spec/extensions/set_overrides_spec.rb +14 -8
  211. data/spec/extensions/sharding_spec.rb +9 -18
  212. data/spec/extensions/shared_caching_spec.rb +3 -4
  213. data/spec/extensions/single_table_inheritance_spec.rb +11 -11
  214. data/spec/extensions/skip_create_refresh_spec.rb +2 -1
  215. data/spec/extensions/spec_helper.rb +1 -1
  216. data/spec/extensions/split_values_spec.rb +2 -2
  217. data/spec/extensions/sql_comments_spec.rb +6 -0
  218. data/spec/extensions/static_cache_spec.rb +7 -9
  219. data/spec/extensions/string_agg_spec.rb +30 -29
  220. data/spec/extensions/tactical_eager_loading_spec.rb +4 -5
  221. data/spec/extensions/thread_local_timezones_spec.rb +2 -2
  222. data/spec/extensions/timestamps_spec.rb +28 -3
  223. data/spec/extensions/to_dot_spec.rb +1 -2
  224. data/spec/extensions/tree_spec.rb +33 -29
  225. data/spec/extensions/typecast_on_load_spec.rb +1 -1
  226. data/spec/extensions/unlimited_update_spec.rb +1 -0
  227. data/spec/extensions/update_primary_key_spec.rb +11 -7
  228. data/spec/extensions/update_refresh_spec.rb +1 -1
  229. data/spec/extensions/uuid_spec.rb +0 -1
  230. data/spec/extensions/validate_associated_spec.rb +1 -1
  231. data/spec/extensions/validation_class_methods_spec.rb +10 -10
  232. data/spec/extensions/validation_helpers_spec.rb +10 -10
  233. data/spec/extensions/xml_serializer_spec.rb +7 -3
  234. data/spec/integration/associations_test.rb +31 -31
  235. data/spec/integration/dataset_test.rb +17 -19
  236. data/spec/integration/eager_loader_test.rb +24 -24
  237. data/spec/integration/model_test.rb +6 -6
  238. data/spec/integration/plugin_test.rb +43 -43
  239. data/spec/integration/prepared_statement_test.rb +6 -6
  240. data/spec/integration/schema_test.rb +63 -52
  241. data/spec/integration/spec_helper.rb +6 -1
  242. data/spec/integration/transaction_test.rb +13 -13
  243. data/spec/model/association_reflection_spec.rb +17 -17
  244. data/spec/model/associations_spec.rb +101 -96
  245. data/spec/model/base_spec.rb +175 -49
  246. data/spec/model/class_dataset_methods_spec.rb +5 -9
  247. data/spec/model/dataset_methods_spec.rb +5 -5
  248. data/spec/model/eager_loading_spec.rb +209 -235
  249. data/spec/model/hooks_spec.rb +15 -15
  250. data/spec/model/model_spec.rb +28 -21
  251. data/spec/model/plugins_spec.rb +4 -5
  252. data/spec/model/record_spec.rb +59 -57
  253. data/spec/model/spec_helper.rb +1 -1
  254. data/spec/model/validations_spec.rb +6 -6
  255. data/spec/spec_config.rb +1 -1
  256. metadata +10 -2
@@ -18,7 +18,7 @@ module Sequel
18
18
 
19
19
  # Which options don't affect the SQL generation. Used by simple_select_all?
20
20
  # to determine if this is a simple SELECT * FROM table.
21
- NON_SQL_OPTIONS = [:server, :graph, :eager, :eager_graph, :graph_aliases].freeze
21
+ NON_SQL_OPTIONS = [:server, :graph, :eager, :eager_graph, :graph_aliases, :row_proc, :quote_identifiers, :identifier_input_method, :identifier_output_method, :skip_symbol_cache, :model, :model_object, :association_reflection, :fetch, :numrows, :autoid].freeze
22
22
 
23
23
  # These symbols have _join methods created (e.g. inner_join) that
24
24
  # call join_table with the symbol, passing along the arguments and
@@ -69,19 +69,44 @@ module Sequel
69
69
  where(*cond, &block)
70
70
  end
71
71
 
72
- # Returns a new clone of the dataset with the given options merged.
73
- # If the options changed include options in COLUMN_CHANGE_OPTS, the cached
74
- # columns are deleted. This method should generally not be called
75
- # directly by user code.
76
- def clone(opts = nil)
77
- c = super()
78
- if opts
79
- c.instance_variable_set(:@opts, Hash[@opts].merge!(opts))
80
- c.instance_variable_set(:@columns, nil) if @columns && !opts.each_key{|o| break if COLUMN_CHANGE_OPTS.include?(o)}
81
- else
82
- c.instance_variable_set(:@opts, Hash[@opts])
72
+ # On Ruby 2.4+, use clone(:freeze=>false) to create clones, because
73
+ # we use true freezing in that case, and we need to modify the opts
74
+ # in the frozen copy.
75
+ #
76
+ # On Ruby <2.4, just use Object#clone directly, since we don't
77
+ # use true freezing as it isn't possible.
78
+ if TRUE_FREEZE
79
+ # Save original clone implementation, as some other methods need
80
+ # to call it internally.
81
+ alias _clone clone
82
+ private :_clone
83
+
84
+ # Returns a new clone of the dataset with the given options merged.
85
+ # If the options changed include options in COLUMN_CHANGE_OPTS, the cached
86
+ # columns are deleted. This method should generally not be called
87
+ # directly by user code.
88
+ def clone(opts = OPTS)
89
+ c = super(:freeze=>false)
90
+ c.opts.merge!(opts)
91
+ unless opts.each_key{|o| break if COLUMN_CHANGE_OPTS.include?(o)}
92
+ c.clear_columns_cache
93
+ end
94
+ c.freeze if frozen?
95
+ c
83
96
  end
84
- c
97
+ else
98
+ # :nocov:
99
+ # :nodoc:
100
+ def clone(opts = OPTS)
101
+ c = super()
102
+ c.opts.merge!(opts)
103
+ unless opts.each_key{|o| break if COLUMN_CHANGE_OPTS.include?(o)}
104
+ c.clear_columns_cache
105
+ end
106
+ c.freeze if frozen?
107
+ c
108
+ end
109
+ # :nocov:
85
110
  end
86
111
 
87
112
  # Returns a copy of the dataset with the SQL DISTINCT clause. The DISTINCT
@@ -97,7 +122,7 @@ module Sequel
97
122
  def distinct(*args, &block)
98
123
  virtual_row_columns(args, block)
99
124
  raise(InvalidOperation, "DISTINCT ON not supported") if !args.empty? && !supports_distinct_on?
100
- clone(:distinct => args)
125
+ clone(:distinct => args.freeze)
101
126
  end
102
127
 
103
128
  # Adds an EXCEPT clause using a second dataset object.
@@ -131,6 +156,22 @@ module Sequel
131
156
  #
132
157
  # DB[:items].exclude(:category => 'software', :id=>3)
133
158
  # # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
159
+ #
160
+ # Also note that SQL uses 3-valued boolean logic (+true+, +false+, +NULL+), so
161
+ # the inverse of a true condition is a false condition, and will still
162
+ # not match rows that were NULL originally. If you take the earlier
163
+ # example:
164
+ #
165
+ # DB[:items].exclude(:category => 'software')
166
+ # # SELECT * FROM items WHERE (category != 'software')
167
+ #
168
+ # Note that this does not match rows where +category+ is +NULL+. This
169
+ # is because +NULL+ is an unknown value, and you do not know whether
170
+ # or not the +NULL+ category is +software+. You can explicitly
171
+ # specify how to handle +NULL+ values if you want:
172
+ #
173
+ # DB[:items].exclude(Sequel.~(:category=>nil) & {:category => 'software'})
174
+ # # SELECT * FROM items WHERE ((category IS NULL) OR (category != 'software'))
134
175
  def exclude(*cond, &block)
135
176
  _filter_or_exclude(true, :where, *cond, &block)
136
177
  end
@@ -139,6 +180,9 @@ module Sequel
139
180
  #
140
181
  # DB[:items].select_group(:name).exclude_having{count(name) < 2}
141
182
  # # SELECT name FROM items GROUP BY name HAVING (count(name) >= 2)
183
+ #
184
+ # See documentation for exclude for how inversion is handled in regards
185
+ # to SQL 3-valued boolean logic.
142
186
  def exclude_having(*cond, &block)
143
187
  _filter_or_exclude(true, :having, *cond, &block)
144
188
  end
@@ -148,13 +192,25 @@ module Sequel
148
192
  exclude(*cond, &block)
149
193
  end
150
194
 
151
- # Return a clone of the dataset loaded with the given dataset extensions.
152
- # If no related extension file exists or the extension does not have
153
- # specific support for Dataset objects, an Error will be raised.
154
- def extension(*exts)
155
- c = clone
156
- c.send(:_extension!, exts)
157
- c
195
+ if TRUE_FREEZE
196
+ # Return a clone of the dataset loaded with the given dataset extensions.
197
+ # If no related extension file exists or the extension does not have
198
+ # specific support for Dataset objects, an Error will be raised.
199
+ def extension(*a)
200
+ c = _clone(:freeze=>false)
201
+ c.send(:_extension!, a)
202
+ c.freeze if frozen?
203
+ c
204
+ end
205
+ else
206
+ # :nocov:
207
+ # :nodoc:
208
+ def extension(*exts)
209
+ c = clone
210
+ c.send(:_extension!, exts)
211
+ c
212
+ end
213
+ # :nocov:
158
214
  end
159
215
 
160
216
  # Alias for where.
@@ -166,7 +222,7 @@ module Sequel
166
222
  #
167
223
  # DB[:table].for_update # SELECT * FROM table FOR UPDATE
168
224
  def for_update
169
- lock_style(:update)
225
+ cached_dataset(:_for_update_ds){lock_style(:update)}
170
226
  end
171
227
 
172
228
  # Returns a copy of the dataset with the source changed. If no
@@ -203,8 +259,8 @@ module Sequel
203
259
  s
204
260
  end
205
261
  end
206
- o = {:from=>source.empty? ? nil : source}
207
- o[:with] = (opts[:with] || []) + ctes if ctes
262
+ o = {:from=>source.empty? ? nil : source.freeze}
263
+ o[:with] = ((opts[:with] || []) + ctes).freeze if ctes
208
264
  o[:num_dataset_sources] = table_alias_num if table_alias_num > 0
209
265
  clone(o)
210
266
  end
@@ -288,7 +344,7 @@ module Sequel
288
344
  # DB[:items].group{[a, sum(b)]} # SELECT * FROM items GROUP BY a, sum(b)
289
345
  def group(*columns, &block)
290
346
  virtual_row_columns(columns, block)
291
- clone(:group => (columns.compact.empty? ? nil : columns))
347
+ clone(:group => (columns.compact.empty? ? nil : columns.freeze))
292
348
  end
293
349
 
294
350
  # Alias of group
@@ -318,7 +374,7 @@ module Sequel
318
374
  # # SELECT substr(first_name, 1, 1) AS initial, count(*) AS count FROM items GROUP BY substr(first_name, 1, 1)
319
375
  # # => [{:initial=>'a', :count=>1}, ...]
320
376
  def group_and_count(*columns, &block)
321
- select_group(*columns, &block).select_more(COUNT_OF_ALL_AS_COUNT)
377
+ select_group(*columns, &block).select_append(COUNT_OF_ALL_AS_COUNT)
322
378
  end
323
379
 
324
380
  # Returns a copy of the dataset with the given columns added to the list of
@@ -389,15 +445,20 @@ module Sequel
389
445
  #
390
446
  # DB[:items].where(:category => 'software', :id=>3).invert
391
447
  # # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
448
+ #
449
+ # See documentation for exclude for how inversion is handled in regards
450
+ # to SQL 3-valued boolean logic.
392
451
  def invert
393
- having, where = @opts.values_at(:having, :where)
394
- if having.nil? && where.nil?
395
- where(false)
396
- else
397
- o = {}
398
- o[:having] = SQL::BooleanExpression.invert(having) if having
399
- o[:where] = SQL::BooleanExpression.invert(where) if where
400
- clone(o)
452
+ cached_dataset(:_invert_ds) do
453
+ having, where = @opts.values_at(:having, :where)
454
+ if having.nil? && where.nil?
455
+ where(false)
456
+ else
457
+ o = {}
458
+ o[:having] = SQL::BooleanExpression.invert(having) if having
459
+ o[:where] = SQL::BooleanExpression.invert(where) if where
460
+ clone(o)
461
+ end
401
462
  end
402
463
  end
403
464
 
@@ -535,7 +596,7 @@ module Sequel
535
596
  SQL::JoinOnClause.new(expr, type, table_expr)
536
597
  end
537
598
 
538
- opts = {:join => (@opts[:join] || []) + [join]}
599
+ opts = {:join => ((@opts[:join] || []) + [join]).freeze}
539
600
  opts[:last_joined_table] = table_name unless options[:reset_implicit_qualifier] == false
540
601
  opts[:num_dataset_sources] = table_alias_num if table_alias_num
541
602
  clone(opts)
@@ -615,7 +676,7 @@ module Sequel
615
676
  # ds.all # => [{2=>:id}]
616
677
  # ds.naked.all # => [{:id=>2}]
617
678
  def naked
618
- with_row_proc(nil)
679
+ cached_dataset(:_naked_ds){with_row_proc(nil)}
619
680
  end
620
681
 
621
682
  # Returns a copy of the dataset with a specified order. Can be safely combined with limit.
@@ -661,12 +722,17 @@ module Sequel
661
722
  # DB[:items].order(nil) # SELECT * FROM items
662
723
  def order(*columns, &block)
663
724
  virtual_row_columns(columns, block)
664
- clone(:order => (columns.compact.empty?) ? nil : columns)
725
+ clone(:order => (columns.compact.empty?) ? nil : columns.freeze)
665
726
  end
666
727
 
667
- # Alias of order_more, for naming consistency with order_prepend.
728
+ # Returns a copy of the dataset with the order columns added
729
+ # to the end of the existing order.
730
+ #
731
+ # DB[:items].order(:a).order(:b) # SELECT * FROM items ORDER BY b
732
+ # DB[:items].order(:a).order_append(:b) # SELECT * FROM items ORDER BY a, b
668
733
  def order_append(*columns, &block)
669
- order_more(*columns, &block)
734
+ columns = @opts[:order] + columns if @opts[:order]
735
+ order(*columns, &block)
670
736
  end
671
737
 
672
738
  # Alias of order
@@ -674,14 +740,9 @@ module Sequel
674
740
  order(*columns, &block)
675
741
  end
676
742
 
677
- # Returns a copy of the dataset with the order columns added
678
- # to the end of the existing order.
679
- #
680
- # DB[:items].order(:a).order(:b) # SELECT * FROM items ORDER BY b
681
- # DB[:items].order(:a).order_more(:b) # SELECT * FROM items ORDER BY a, b
743
+ # Alias of order_append.
682
744
  def order_more(*columns, &block)
683
- columns = @opts[:order] + columns if @opts[:order]
684
- order(*columns, &block)
745
+ order_append(*columns, &block)
685
746
  end
686
747
 
687
748
  # Returns a copy of the dataset with the order columns added
@@ -691,7 +752,7 @@ module Sequel
691
752
  # DB[:items].order(:a).order_prepend(:b) # SELECT * FROM items ORDER BY b, a
692
753
  def order_prepend(*columns, &block)
693
754
  ds = order(*columns, &block)
694
- @opts[:order] ? ds.order_more(*@opts[:order]) : ds
755
+ @opts[:order] ? ds.order_append(*@opts[:order]) : ds
695
756
  end
696
757
 
697
758
  # Qualify to the given table, or first source if no table is given.
@@ -708,7 +769,7 @@ module Sequel
708
769
  (o.keys & QUALIFY_KEYS).each do |k|
709
770
  h[k] = qualified_expression(o[k], table)
710
771
  end
711
- h[:select] = [SQL::ColumnAll.new(table)] if !o[:select] || o[:select].empty?
772
+ h[:select] = [SQL::ColumnAll.new(table)].freeze if !o[:select] || o[:select].empty?
712
773
  clone(h)
713
774
  end
714
775
 
@@ -722,7 +783,7 @@ module Sequel
722
783
  # DB[:items].returning(:id, :name) # RETURNING id, name
723
784
  def returning(*values)
724
785
  raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
725
- clone(:returning=>values)
786
+ clone(:returning=>values.freeze)
726
787
  end
727
788
 
728
789
  # Returns a copy of the dataset with the order reversed. If no order is
@@ -733,8 +794,12 @@ module Sequel
733
794
  # DB[:items].order(:id).reverse # SELECT * FROM items ORDER BY id DESC
734
795
  # DB[:items].order(:id).reverse(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name ASC
735
796
  def reverse(*order, &block)
736
- virtual_row_columns(order, block)
737
- order(*invert_order(order.empty? ? @opts[:order] : order))
797
+ if order.empty? && !block
798
+ cached_dataset(:_reverse_ds){order(*invert_order(@opts[:order]))}
799
+ else
800
+ virtual_row_columns(order, block)
801
+ order(*invert_order(order.empty? ? @opts[:order] : order.freeze))
802
+ end
738
803
  end
739
804
 
740
805
  # Alias of +reverse+
@@ -751,7 +816,7 @@ module Sequel
751
816
  # DB[:items].select{[a, sum(b)]} # SELECT a, sum(b) FROM items
752
817
  def select(*columns, &block)
753
818
  virtual_row_columns(columns, block)
754
- clone(:select => columns)
819
+ clone(:select => columns.freeze)
755
820
  end
756
821
 
757
822
  # Returns a copy of the dataset selecting the wildcard if no arguments
@@ -765,7 +830,7 @@ module Sequel
765
830
  if tables.empty?
766
831
  clone(:select => nil)
767
832
  else
768
- select(*tables.map{|t| i, a = split_alias(t); a || i}.map{|t| SQL::ColumnAll.new(t)})
833
+ select(*tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze)
769
834
  end
770
835
  end
771
836
 
@@ -780,7 +845,7 @@ module Sequel
780
845
  cur_sel = @opts[:select]
781
846
  if !cur_sel || cur_sel.empty?
782
847
  unless supports_select_all_and_column?
783
- return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_more(*columns, &block)
848
+ return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_append(*columns, &block)
784
849
  end
785
850
  cur_sel = [WILDCARD]
786
851
  end
@@ -832,8 +897,10 @@ module Sequel
832
897
 
833
898
  # Skip locked rows when returning results from this dataset.
834
899
  def skip_locked
835
- raise(Error, 'This dataset does not support skipping locked rows') unless supports_skip_locked?
836
- clone(:skip_locked=>true)
900
+ cached_dataset(:_skip_locked_ds) do
901
+ raise(Error, 'This dataset does not support skipping locked rows') unless supports_skip_locked?
902
+ clone(:skip_locked=>true)
903
+ end
837
904
  end
838
905
 
839
906
  # Unbind bound variables from this dataset's filter and return an array of two
@@ -857,7 +924,7 @@ module Sequel
857
924
  # DB[:items].group(:a).having(:a=>1).where(:b).unfiltered
858
925
  # # SELECT * FROM items GROUP BY a
859
926
  def unfiltered
860
- clone(:where => nil, :having => nil)
927
+ cached_dataset(:_unfiltered_ds){clone(:where => nil, :having => nil)}
861
928
  end
862
929
 
863
930
  # Returns a copy of the dataset with no grouping (GROUP or HAVING clause) applied.
@@ -865,7 +932,7 @@ module Sequel
865
932
  # DB[:items].group(:a).having(:a=>1).where(:b).ungrouped
866
933
  # # SELECT * FROM items WHERE b
867
934
  def ungrouped
868
- clone(:group => nil, :having => nil)
935
+ cached_dataset(:_ungrouped_ds){clone(:group => nil, :having => nil)}
869
936
  end
870
937
 
871
938
  # Adds a UNION clause using a second dataset object.
@@ -892,14 +959,14 @@ module Sequel
892
959
  #
893
960
  # DB[:items].limit(10, 20).unlimited # SELECT * FROM items
894
961
  def unlimited
895
- clone(:limit=>nil, :offset=>nil)
962
+ cached_dataset(:_unlimited_ds){clone(:limit=>nil, :offset=>nil)}
896
963
  end
897
964
 
898
965
  # Returns a copy of the dataset with no order.
899
966
  #
900
967
  # DB[:items].order(:a).unordered # SELECT * FROM items
901
968
  def unordered
902
- order(nil)
969
+ cached_dataset(:_unordered_ds){clone(:order=>nil)}
903
970
  end
904
971
 
905
972
  # Returns a copy of the dataset with the given WHERE conditions imposed upon it.
@@ -970,7 +1037,7 @@ module Sequel
970
1037
  s, ds = hoist_cte(dataset)
971
1038
  s.with(name, ds, opts)
972
1039
  else
973
- clone(:with=>(@opts[:with]||[]) + [Hash[opts].merge!(:name=>name, :dataset=>dataset)])
1040
+ clone(:with=>((@opts[:with]||[]) + [Hash[opts].merge!(:name=>name, :dataset=>dataset)]).freeze)
974
1041
  end
975
1042
  end
976
1043
 
@@ -999,15 +1066,33 @@ module Sequel
999
1066
  s, ds = hoist_cte(recursive)
1000
1067
  s.with_recursive(name, nonrecursive, ds, opts)
1001
1068
  else
1002
- clone(:with=>(@opts[:with]||[]) + [Hash[opts].merge!(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))])
1069
+ clone(:with=>((@opts[:with]||[]) + [Hash[opts].merge!(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))]).freeze)
1003
1070
  end
1004
1071
  end
1005
1072
 
1006
- # Return a clone of the dataset extended with the given modules.
1007
- def with_extend(*mods)
1008
- c = clone
1009
- c.extend(*mods)
1010
- c
1073
+ if TRUE_FREEZE
1074
+ # Return a clone of the dataset extended with the given modules.
1075
+ # Note that like Object#extend, when multiple modules are provided
1076
+ # as arguments the cloned dataset is extended with the modules in reverse
1077
+ # order. If a block is provided, a module is created using the block and
1078
+ # the clone is extended with that module after any modules given as arguments.
1079
+ def with_extend(*mods, &block)
1080
+ c = _clone(:freeze=>false)
1081
+ c.extend(*mods) unless mods.empty?
1082
+ c.extend(Module.new(&block)) if block
1083
+ c.freeze if frozen?
1084
+ c
1085
+ end
1086
+ else
1087
+ # :nocov:
1088
+ # :nodoc:
1089
+ def with_extend(*mods, &block)
1090
+ c = clone
1091
+ c.extend(*mods) unless mods.empty?
1092
+ c.extend(Module.new(&block)) if block
1093
+ c
1094
+ end
1095
+ # :nocov:
1011
1096
  end
1012
1097
 
1013
1098
  # Returns a cloned dataset with the given row_proc.
@@ -1016,9 +1101,7 @@ module Sequel
1016
1101
  # ds.all # => [{:id=>2}]
1017
1102
  # ds.with_row_proc(proc(&:invert)).all # => [{2=>:id}]
1018
1103
  def with_row_proc(callable)
1019
- c = clone
1020
- c.instance_variable_set(:@row_proc, callable)
1021
- c
1104
+ clone(:row_proc=>callable)
1022
1105
  end
1023
1106
 
1024
1107
  # Returns a copy of the dataset with the static SQL used. This is useful if you want
@@ -1050,7 +1133,7 @@ module Sequel
1050
1133
  s, ds = hoist_cte(dataset)
1051
1134
  return s.compound_clone(type, ds, opts)
1052
1135
  end
1053
- ds = compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map(&:dup) + [[type, dataset.compound_from_self, opts[:all]]])
1136
+ ds = compound_from_self.clone(:compounds=>(Array(@opts[:compounds]).map(&:dup) + [[type, dataset.compound_from_self, opts[:all]].freeze]).freeze)
1054
1137
  opts[:from_self] == false ? ds : ds.from_self(opts)
1055
1138
  end
1056
1139
 
@@ -1152,7 +1235,7 @@ module Sequel
1152
1235
  # clause from the given dataset added to it, and the second a clone of
1153
1236
  # the given dataset with the WITH clause removed.
1154
1237
  def hoist_cte(ds)
1155
- [clone(:with => (opts[:with] || []) + ds.opts[:with]), ds.clone(:with => nil)]
1238
+ [clone(:with => ((opts[:with] || []) + ds.opts[:with]).freeze), ds.clone(:with => nil)]
1156
1239
  end
1157
1240
 
1158
1241
  # Whether CTEs need to be hoisted from the given ds into the current ds.
@@ -198,11 +198,13 @@ module Sequel
198
198
  # being an array of symbol/strings for the appropriate branch.
199
199
  def self.def_sql_method(mod, type, clauses)
200
200
  priv = type == :update || type == :insert
201
+ cacheable = type == :select || type == :delete
201
202
 
202
203
  lines = []
203
204
  lines << 'private' if priv
204
205
  lines << "def #{'_' if priv}#{type}_sql"
205
206
  lines << 'if sql = opts[:sql]; return static_sql(sql) end' unless priv
207
+ lines << "if sql = cache_get(:_#{type}_sql); return sql end" if cacheable
206
208
  lines << 'check_modification_allowed!' if type == :delete
207
209
  lines << 'sql = @opts[:append_sql] || sql_string_origin'
208
210
 
@@ -216,6 +218,7 @@ module Sequel
216
218
  lines.concat(clause_methods(type, clauses).map{|x| "#{x}(sql)"})
217
219
  end
218
220
 
221
+ lines << "cache_set(:_#{type}_sql, sql) if cache_sql?" if cacheable
219
222
  lines << 'sql'
220
223
  lines << 'end'
221
224
 
@@ -522,6 +525,11 @@ module Sequel
522
525
  # Append literalization of delayed evaluation to SQL string,
523
526
  # causing the delayed evaluation proc to be evaluated.
524
527
  def delayed_evaluation_sql_append(sql, delay)
528
+ # Delayed evaluations are used specifically so the SQL
529
+ # can differ in subsequent calls, so we definitely don't
530
+ # want to cache the sql in this case.
531
+ disable_sql_caching!
532
+
525
533
  if recorder = @opts[:placeholder_literalizer]
526
534
  recorder.use(sql, lambda{delay.call(self)}, nil)
527
535
  else
@@ -901,6 +909,12 @@ module Sequel
901
909
  end
902
910
  end
903
911
 
912
+ # Only allow caching the select SQL if the dataset is frozen and hasn't
913
+ # specifically been marked as not allowing SQL caching.
914
+ def cache_sql?
915
+ frozen? && !@opts[:no_cache_sql] && !cache_get(:_no_cache_sql)
916
+ end
917
+
904
918
  # Raise an InvalidOperation exception if deletion is not allowed
905
919
  # for this dataset
906
920
  def check_modification_allowed!
@@ -993,6 +1007,11 @@ module Sequel
993
1007
  end
994
1008
  end
995
1009
 
1010
+ # Disable caching of SQL for the current dataset
1011
+ def disable_sql_caching!
1012
+ cache_set(:_no_cache_sql, true)
1013
+ end
1014
+
996
1015
  # An SQL FROM clause to use in SELECT statements where the dataset has
997
1016
  # no from tables.
998
1017
  def empty_from_sql
@@ -1099,10 +1118,9 @@ module Sequel
1099
1118
  end
1100
1119
  end
1101
1120
 
1102
- # Modify the identifier returned from the database based on the
1103
- # identifier_output_method.
1121
+ # Upcase identifiers by default when inputting them into the database.
1104
1122
  def input_identifier(v)
1105
- (i = identifier_input_method) ? v.to_s.send(i) : v.to_s
1123
+ v.to_s.upcase
1106
1124
  end
1107
1125
 
1108
1126
  def insert_into_sql(sql)
@@ -1250,6 +1268,10 @@ module Sequel
1250
1268
  # calls +sql_literal+ if object responds to it, otherwise raises an error.
1251
1269
  # If a database specific type is allowed, this should be overriden in a subclass.
1252
1270
  def literal_other_append(sql, v)
1271
+ # We can't be sure if v will always literalize to the same SQL, so
1272
+ # don't cache SQL for a dataset that uses this.
1273
+ disable_sql_caching!
1274
+
1253
1275
  if v.respond_to?(:sql_literal_append)
1254
1276
  v.sql_literal_append(self, sql)
1255
1277
  elsif v.respond_to?(:sql_literal)
@@ -1496,12 +1518,7 @@ module Sequel
1496
1518
 
1497
1519
  # Whether the symbol cache should be skipped when literalizing the dataset
1498
1520
  def skip_symbol_cache?
1499
- @skip_symbol_cache
1500
- end
1501
-
1502
- # Set the dataset to skip the symbol cache
1503
- def skip_symbol_cache!
1504
- @skip_symbol_cache = true
1521
+ @opts[:skip_symbol_cache]
1505
1522
  end
1506
1523
 
1507
1524
  # Append literalization of array of sources/tables to SQL string, raising an Error if there