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
@@ -115,6 +115,12 @@ module Sequel
115
115
  true
116
116
  end
117
117
 
118
+ # Whether this dataset considers unquoted identifiers as uppercase. True
119
+ # by default as that is the SQL standard
120
+ def folds_unquoted_identifiers_to_uppercase?
121
+ true
122
+ end
123
+
118
124
  # Whether the database supports combining multiple alter table
119
125
  # operations into a single query, false by default.
120
126
  def supports_combining_alter_table_ops?
@@ -30,7 +30,10 @@ module Sequel
30
30
  # included in the database, the identifier mangling defaults are reset correctly.
31
31
  module ResetIdentifierMangling
32
32
  def extended(obj)
33
- obj.send(:reset_identifier_mangling)
33
+ # :nocov:
34
+ Sequel::Deprecation.deprecate("Sequel::Database::ResetIdentifierMangling is no longer needed and will be removed in Sequel 5. Please update your adapter.")
35
+ obj.send(:reset_identifier_mangling) if obj.respond_to?(:reset_identifier_mangling)
36
+ # :nocov:
34
37
  end
35
38
  end
36
39
 
@@ -104,8 +107,7 @@ module Sequel
104
107
  #
105
108
  # Accepts the following options:
106
109
  # :default_string_column_size :: The default size of string columns, 255 by default.
107
- # :identifier_input_method :: A string method symbol to call on identifiers going into the database.
108
- # :identifier_output_method :: A string method symbol to call on identifiers coming from the database.
110
+ # :identifier_mangling :: Whether to support non-default identifier mangling for the current database.
109
111
  # :logger :: A specific logger to use.
110
112
  # :loggers :: An array of loggers to use.
111
113
  # :name :: A name to use for the Database object.
@@ -131,9 +133,6 @@ module Sequel
131
133
  @transactions = {}
132
134
  @symbol_literal_cache = {}
133
135
 
134
- @identifier_input_method = nil
135
- @identifier_output_method = nil
136
- @quote_identifiers = nil
137
136
  @timezone = nil
138
137
 
139
138
  @dataset_class = dataset_class_default
@@ -147,8 +146,11 @@ module Sequel
147
146
 
148
147
  @pool = ConnectionPool.get_pool(self, @opts)
149
148
 
150
- reset_identifier_mangling
149
+ reset_default_dataset
151
150
  adapter_initialize
151
+ if typecast_value_boolean(@opts.fetch(:identifier_mangling, true))
152
+ extension(:identifier_mangling)
153
+ end
152
154
 
153
155
  unless typecast_value_boolean(@opts[:keep_reference]) == false
154
156
  Sequel.synchronize{::Sequel::DATABASES.push(self)}
@@ -248,7 +250,6 @@ module Sequel
248
250
 
249
251
  # Cache the prepared statement object at the given name.
250
252
  def set_prepared_statement(name, ps)
251
- ps.prepared_sql
252
253
  Sequel.synchronize{prepared_statements[name] = ps}
253
254
  end
254
255
 
@@ -429,15 +430,15 @@ module Sequel
429
430
  def typecast_value_datetime(value)
430
431
  Sequel.typecast_to_application_timestamp(value)
431
432
  end
432
-
433
- # Typecast the value to a BigDecimal
434
- def typecast_value_decimal(value)
435
- case value
436
- when BigDecimal
437
- value
438
- when Numeric
439
- BigDecimal.new(value.to_s)
440
- when String
433
+
434
+ if RUBY_VERSION >= '2.4'
435
+ # Typecast a string to a BigDecimal
436
+ def _typecast_value_string_to_decimal(value)
437
+ BigDecimal.new(value)
438
+ end
439
+ else
440
+ # :nocov:
441
+ def _typecast_value_string_to_decimal(value)
441
442
  d = BigDecimal.new(value)
442
443
  if d.zero?
443
444
  # BigDecimal parsing is loose by default, returning a 0 value for
@@ -450,6 +451,19 @@ module Sequel
450
451
  end
451
452
  end
452
453
  d
454
+ end
455
+ # :nocov:
456
+ end
457
+
458
+ # Typecast the value to a BigDecimal
459
+ def typecast_value_decimal(value)
460
+ case value
461
+ when BigDecimal
462
+ value
463
+ when Numeric
464
+ BigDecimal.new(value.to_s)
465
+ when String
466
+ _typecast_value_string_to_decimal(value)
453
467
  else
454
468
  raise InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
455
469
  end
@@ -32,7 +32,7 @@ module Sequel
32
32
  # Call the prepared statement with the given name with the given hash
33
33
  # of arguments.
34
34
  #
35
- # DB[:items].filter(:id=>1).prepare(:first, :sa)
35
+ # DB[:items].where(:id=>1).prepare(:first, :sa)
36
36
  # DB.call(:sa) # SELECT * FROM items WHERE id = 1
37
37
  def call(ps_name, hash={}, &block)
38
38
  prepared_statement(ps_name).call(hash, &block)
@@ -287,13 +287,16 @@ module Sequel
287
287
  (ds || dataset).method(:input_identifier)
288
288
  end
289
289
 
290
+ # Uncached version of metadata_dataset, designed for overriding.
291
+ def _metadata_dataset
292
+ dataset
293
+ end
294
+
290
295
  # Return a dataset that uses the default identifier input and output methods
291
296
  # for this database. Used when parsing metadata so that column symbols are
292
297
  # returned as expected.
293
298
  def metadata_dataset
294
- @metadata_dataset ||= dataset.
295
- with_identifier_input_method(identifier_input_method_default).
296
- with_identifier_output_method(identifier_output_method_default)
299
+ @metadata_dataset ||= _metadata_dataset
297
300
  end
298
301
 
299
302
  # Return a Method object for the dataset's output_identifier_method.
@@ -235,7 +235,7 @@ module Sequel
235
235
  # Creates a view, replacing a view with the same name if one already exists.
236
236
  #
237
237
  # DB.create_or_replace_view(:some_items, "SELECT * FROM items WHERE price < 100")
238
- # DB.create_or_replace_view(:some_items, DB[:items].filter(:category => 'ruby'))
238
+ # DB.create_or_replace_view(:some_items, DB[:items].where(:category => 'ruby'))
239
239
  #
240
240
  # For databases where replacing a view is not natively supported, support
241
241
  # is emulated by dropping a view with the same name before creating the view.
@@ -8,7 +8,7 @@ module Sequel
8
8
  # Query results are always retrieved on demand, so a dataset can be kept
9
9
  # around and reused indefinitely (datasets never cache results):
10
10
  #
11
- # my_posts = DB[:posts].filter(:author => 'david') # no records are retrieved
11
+ # my_posts = DB[:posts].where(:author => 'david') # no records are retrieved
12
12
  # my_posts.all # records are retrieved
13
13
  # my_posts.all # records are retrieved again
14
14
  #
@@ -16,9 +16,9 @@ module Sequel
16
16
  # reuse different datasets to access data:
17
17
  #
18
18
  # posts = DB[:posts]
19
- # davids_posts = posts.filter(:author => 'david')
20
- # old_posts = posts.filter('stamp < ?', Date.today - 7)
21
- # davids_old_posts = davids_posts.filter('stamp < ?', Date.today - 7)
19
+ # davids_posts = posts.where(:author => 'david')
20
+ # old_posts = posts.where('stamp < ?', Date.today - 7)
21
+ # davids_old_posts = davids_posts.where('stamp < ?', Date.today - 7)
22
22
  #
23
23
  # Datasets are Enumerable objects, so they can be manipulated using any
24
24
  # of the Enumerable methods, such as map, inject, etc.
@@ -27,6 +27,10 @@ module Sequel
27
27
  class Dataset
28
28
  OPTS = Sequel::OPTS
29
29
 
30
+ # Whether Dataset#freeze can actually freeze datasets. True only on ruby 2.4+,
31
+ # as it requires clone(freeze: false)
32
+ TRUE_FREEZE = RUBY_VERSION >= '2.4'
33
+
30
34
  include Enumerable
31
35
  include SQL::AliasMethods
32
36
  include SQL::BooleanMethods
@@ -36,10 +40,6 @@ module Sequel
36
40
  include SQL::NumericMethods
37
41
  include SQL::OrderMethods
38
42
  include SQL::StringMethods
39
-
40
- private
41
-
42
- attr_writer :columns
43
43
  end
44
44
 
45
45
  require(%w"query actions features graph prepared_statements misc mutation sql placeholder_literalizer", 'dataset')
@@ -17,6 +17,9 @@ module Sequel
17
17
  single_record single_record! single_value single_value! sum to_hash to_hash_groups truncate update
18
18
  METHS
19
19
 
20
+ # The clone options to use when retriveing columns for a dataset.
21
+ COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 1, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
22
+
20
23
  # Inserts the given argument into the database. Returns self so it
21
24
  # can be used safely when chaining:
22
25
  #
@@ -54,8 +57,8 @@ module Sequel
54
57
  # # => 3
55
58
  # DB[:table].avg{function(column)} # SELECT avg(function(column)) FROM table LIMIT 1
56
59
  # # => 1
57
- def avg(column=Sequel.virtual_row(&Proc.new))
58
- aggregate_dataset.get{avg(column).as(:avg)}
60
+ def avg(arg=Sequel.virtual_row(&Proc.new))
61
+ _aggregate(:avg, arg)
59
62
  end
60
63
 
61
64
  # Returns the columns in the result set in order as an array of symbols.
@@ -68,11 +71,7 @@ module Sequel
68
71
  # DB[:table].columns
69
72
  # # => [:id, :name]
70
73
  def columns
71
- return @columns if @columns
72
- ds = unfiltered.unordered.naked.clone(:distinct => nil, :limit => 1, :offset=>nil)
73
- ds.each{break}
74
- @columns = ds.instance_variable_get(:@columns)
75
- @columns || []
74
+ _columns || columns!
76
75
  end
77
76
 
78
77
  # Ignore any cached column information and perform a query to retrieve
@@ -81,8 +80,14 @@ module Sequel
81
80
  # DB[:table].columns!
82
81
  # # => [:id, :name]
83
82
  def columns!
84
- self.columns = nil
85
- columns
83
+ ds = clone(COLUMNS_CLONE_OPTIONS)
84
+ ds.each{break}
85
+
86
+ if cols = ds.cache[:_columns]
87
+ self.columns = cols
88
+ else
89
+ []
90
+ end
86
91
  end
87
92
 
88
93
  # Returns the number of records in the dataset. If an argument is provided,
@@ -97,20 +102,23 @@ module Sequel
97
102
  # DB[:table].count{foo(column)} # SELECT count(foo(column)) AS count FROM table LIMIT 1
98
103
  # # => 1
99
104
  def count(arg=(no_arg=true), &block)
100
- if no_arg
105
+ if no_arg && !block
106
+ cached_dataset(:_count_ds) do
107
+ aggregate_dataset.select{count{}.*.as(:count)}.single_value_ds
108
+ end.single_value!.to_i
109
+ else
101
110
  if block
102
- arg = Sequel.virtual_row(&block)
103
- aggregate_dataset.get{count(arg).as(:count)}
104
- else
105
- aggregate_dataset.get{count{}.*.as(:count)}.to_i
111
+ if no_arg
112
+ arg = Sequel.virtual_row(&block)
113
+ else
114
+ raise Error, 'cannot provide both argument and block to Dataset#count'
115
+ end
106
116
  end
107
- elsif block
108
- raise Error, 'cannot provide both argument and block to Dataset#count'
109
- else
110
- aggregate_dataset.get{count(arg).as(:count)}
117
+
118
+ _aggregate(:count, arg)
111
119
  end
112
120
  end
113
-
121
+
114
122
  # Deletes the records in the dataset. The returned value should be
115
123
  # number of records deleted, but that is adapter dependent.
116
124
  #
@@ -148,8 +156,9 @@ module Sequel
148
156
  # DB[:table].empty? # SELECT 1 AS one FROM table LIMIT 1
149
157
  # # => false
150
158
  def empty?
151
- ds = @opts[:order] ? unordered : self
152
- ds.get(Sequel::SQL::AliasedExpression.new(1, :one)).nil?
159
+ cached_dataset(:_empty_ds) do
160
+ single_value_ds.unordered.select(Sequel::SQL::AliasedExpression.new(1, :one))
161
+ end.single_value!.nil?
153
162
  end
154
163
 
155
164
  # If a integer argument is given, it is interpreted as a limit, and then returns all
@@ -188,17 +197,46 @@ module Sequel
188
197
  # DB[:table].first(2){id < 2} # SELECT * FROM table WHERE (id < 2) LIMIT 2
189
198
  # # => [{:id=>1}]
190
199
  def first(*args, &block)
191
- ds = block ? filter(&block) : self
200
+ case args.length
201
+ when 0
202
+ unless block
203
+ return single_record
204
+ end
205
+ when 1
206
+ arg = args[0]
207
+ if arg.is_a?(Integer)
208
+ res = if block
209
+ if loader = cached_placeholder_literalizer(:_first_integer_cond_loader) do |pl|
210
+ where(pl.arg).limit(pl.arg)
211
+ end
192
212
 
193
- if args.empty?
194
- ds.single_record
195
- else
196
- args = (args.size == 1) ? args.first : args
197
- if args.is_a?(Integer)
198
- ds.limit(args).all
199
- else
200
- ds.filter(args).single_record
213
+ loader.all(filter_expr(&block), arg)
214
+ else
215
+ where(&block).limit(arg).all
216
+ end
217
+ else
218
+ if loader = cached_placeholder_literalizer(:_first_integer_loader) do |pl|
219
+ limit(pl.arg)
220
+ end
221
+
222
+ loader.all(arg)
223
+ else
224
+ limit(arg).all
225
+ end
226
+ end
227
+
228
+ return res
201
229
  end
230
+ args = arg
231
+ end
232
+
233
+ if loader = cached_placeholder_literalizer(:_first_cond_loader) do |pl|
234
+ _single_record_ds.where(pl.arg)
235
+ end
236
+
237
+ loader.first(filter_expr(args, &block))
238
+ else
239
+ _single_record_ds.where(args, &block).single_record!
202
240
  end
203
241
  end
204
242
 
@@ -234,10 +272,27 @@ module Sequel
234
272
  column = ds.opts[:select]
235
273
  column = nil if column.is_a?(Array) && column.length < 2
236
274
  else
237
- ds = if column.is_a?(Array)
238
- ds.select(*column)
275
+ case column
276
+ when Array
277
+ ds = ds.select(*column)
278
+ when LiteralString, Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression
279
+ if loader = cached_placeholder_literalizer(:_get_loader) do |pl|
280
+ ds.single_value_ds.select(pl.arg)
281
+ end
282
+
283
+ return loader.get(column)
284
+ end
285
+
286
+ ds = ds.select(column)
239
287
  else
240
- ds.select(auto_alias_expression(column))
288
+ if loader = cached_placeholder_literalizer(:_get_alias_loader) do |pl|
289
+ ds.single_value_ds.select(Sequel.as(pl.arg, :v))
290
+ end
291
+
292
+ return loader.get(column)
293
+ end
294
+
295
+ ds = ds.select(Sequel.as(column, :v))
241
296
  end
242
297
  end
243
298
 
@@ -347,7 +402,15 @@ module Sequel
347
402
  # DB[:table].interval{function(column)} # SELECT (max(function(column)) - min(function(column))) FROM table LIMIT 1
348
403
  # # => 7
349
404
  def interval(column=Sequel.virtual_row(&Proc.new))
350
- aggregate_dataset.get{(max(column) - min(column)).as(:interval)}
405
+ if loader = cached_placeholder_literalizer(:_interval_loader) do |pl|
406
+ arg = pl.arg
407
+ aggregate_dataset.limit(1).select((SQL::Function.new(:max, arg) - SQL::Function.new(:min, arg)).as(:interval))
408
+ end
409
+
410
+ loader.get(column)
411
+ else
412
+ aggregate_dataset.get{(max(column) - min(column)).as(:interval)}
413
+ end
351
414
  end
352
415
 
353
416
  # Reverses the order and then runs #first with the given arguments and block. Note that this
@@ -400,8 +463,8 @@ module Sequel
400
463
  # # => 10
401
464
  # DB[:table].max{function(column)} # SELECT max(function(column)) FROM table LIMIT 1
402
465
  # # => 7
403
- def max(column=Sequel.virtual_row(&Proc.new))
404
- aggregate_dataset.get{max(column).as(:max)}
466
+ def max(arg=Sequel.virtual_row(&Proc.new))
467
+ _aggregate(:max, arg)
405
468
  end
406
469
 
407
470
  # Returns the minimum value for the given column/expression.
@@ -411,8 +474,8 @@ module Sequel
411
474
  # # => 1
412
475
  # DB[:table].min{function(column)} # SELECT min(function(column)) FROM table LIMIT 1
413
476
  # # => 0
414
- def min(column=Sequel.virtual_row(&Proc.new))
415
- aggregate_dataset.get{min(column).as(:min)}
477
+ def min(arg=Sequel.virtual_row(&Proc.new))
478
+ _aggregate(:min, arg)
416
479
  end
417
480
 
418
481
  # This is a front end for import that allows you to submit an array of
@@ -552,7 +615,17 @@ module Sequel
552
615
  # DB[:table].interval{function(column)} # SELECT max(function(column)) AS v1, min(function(column)) AS v2 FROM table LIMIT 1
553
616
  # # => 0..7
554
617
  def range(column=Sequel.virtual_row(&Proc.new))
555
- if r = aggregate_dataset.select{[min(column).as(v1), max(column).as(v2)]}.first
618
+ r = if loader = cached_placeholder_literalizer(:_range_loader) do |pl|
619
+ arg = pl.arg
620
+ aggregate_dataset.limit(1).select(SQL::Function.new(:min, arg).as(:v1), SQL::Function.new(:max, arg).as(:v2))
621
+ end
622
+
623
+ loader.first(column)
624
+ else
625
+ aggregate_dataset.select{[min(column).as(v1), max(column).as(v2)]}.first
626
+ end
627
+
628
+ if r
556
629
  (r[:v1]..r[:v2])
557
630
  end
558
631
  end
@@ -649,7 +722,7 @@ module Sequel
649
722
  # DB[:test].single_record # SELECT * FROM test LIMIT 1
650
723
  # # => {:column_name=>'value'}
651
724
  def single_record
652
- clone(:limit=>1).single_record!
725
+ _single_record_ds.single_record!
653
726
  end
654
727
 
655
728
  # Returns the first record in dataset, without limiting the dataset. Returns nil if
@@ -671,9 +744,10 @@ module Sequel
671
744
  # DB[:test].single_value # SELECT * FROM test LIMIT 1
672
745
  # # => 'value'
673
746
  def single_value
674
- if r = ungraphed.naked.single_record
747
+ single_value_ds.each do |r|
675
748
  r.each{|_, v| return v}
676
749
  end
750
+ nil
677
751
  end
678
752
 
679
753
  # Returns the first value of the first record in the dataset, without limiting the dataset.
@@ -695,8 +769,8 @@ module Sequel
695
769
  # # => 55
696
770
  # DB[:table].sum{function(column)} # SELECT sum(function(column)) FROM table LIMIT 1
697
771
  # # => 10
698
- def sum(column=Sequel.virtual_row(&Proc.new))
699
- aggregate_dataset.get{sum(column).as(:sum)}
772
+ def sum(arg=Sequel.virtual_row(&Proc.new))
773
+ _aggregate(:sum, arg)
700
774
  end
701
775
 
702
776
  # Returns a hash with one column used as key and another used as value.
@@ -902,6 +976,11 @@ module Sequel
902
976
  map{|r| r.values.first}
903
977
  end
904
978
 
979
+ # A dataset for returning single values from the current dataset.
980
+ def single_value_ds
981
+ clone(:limit=>1).ungraphed.naked
982
+ end
983
+
905
984
  private
906
985
 
907
986
  # Internals of all and with_sql_all
@@ -913,6 +992,17 @@ module Sequel
913
992
  a
914
993
  end
915
994
 
995
+ # Cached placeholder literalizer for methods that return values using aggregate functions.
996
+ def _aggregate(function, arg)
997
+ if loader = cached_placeholder_literalizer(:"_#{function}_loader") do |pl|
998
+ aggregate_dataset.limit(1).select(SQL::Function.new(function, pl.arg).as(function))
999
+ end
1000
+ loader.get(arg)
1001
+ else
1002
+ aggregate_dataset.get(SQL::Function.new(function, arg).as(function))
1003
+ end
1004
+ end
1005
+
916
1006
  # Internals of +select_hash+ and +select_hash_groups+
917
1007
  def _select_hash(meth, key_column, value_column, opts=OPTS)
918
1008
  select(*(key_column.is_a?(Array) ? key_column : [key_column]) + (value_column.is_a?(Array) ? value_column : [value_column])).
@@ -933,6 +1023,11 @@ module Sequel
933
1023
  end
934
1024
  end
935
1025
 
1026
+ # A cached dataset for a single record for this dataset.
1027
+ def _single_record_ds
1028
+ cached_dataset(:_single_record_ds){clone(:limit=>1)}
1029
+ end
1030
+
936
1031
  # Automatically alias the given expression if it does not have an identifiable alias.
937
1032
  def auto_alias_expression(v)
938
1033
  case v
@@ -1052,11 +1147,10 @@ module Sequel
1052
1147
  Sequel.|(*cond)
1053
1148
  end
1054
1149
 
1055
- # Modify the identifier returned from the database based on the
1056
- # identifier_output_method.
1150
+ # Downcase identifiers by default when outputing them from the database.
1057
1151
  def output_identifier(v)
1058
1152
  v = 'untitled' if v == ''
1059
- (i = identifier_output_method) ? v.to_s.send(i).to_sym : v.to_sym
1153
+ v.to_s.downcase.to_sym
1060
1154
  end
1061
1155
 
1062
1156
  # This is run inside .all, after all of the records have been loaded