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
@@ -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