sequel 4.41.0 → 4.42.0

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