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
@@ -10,11 +10,7 @@ module Sequel
10
10
 
11
11
  # Whether this dataset quotes identifiers.
12
12
  def quote_identifiers?
13
- if defined?(@quote_identifiers)
14
- @quote_identifiers
15
- else
16
- @quote_identifiers = db.quote_identifiers?
17
- end
13
+ @opts.fetch(:quote_identifiers, true)
18
14
  end
19
15
 
20
16
  # Whether this dataset will provide accurate number of rows matched for
@@ -11,7 +11,7 @@ module Sequel
11
11
 
12
12
  # Adds the given graph aliases to the list of graph aliases to use,
13
13
  # unlike +set_graph_aliases+, which replaces the list (the equivalent
14
- # of +select_more+ when graphing). See +set_graph_aliases+.
14
+ # of +select_append+ when graphing). See +set_graph_aliases+.
15
15
  #
16
16
  # DB[:table].add_graph_aliases(:some_alias=>[:table, :column])
17
17
  # # SELECT ..., table.column AS some_alias
@@ -20,7 +20,7 @@ module Sequel
20
20
  raise Error, "cannot call add_graph_aliases on a dataset that has not been called with graph or set_graph_aliases"
21
21
  end
22
22
  columns, graph_aliases = graph_alias_columns(graph_aliases)
23
- select_more(*columns).clone(:graph_aliases => Hash[ga].merge!(graph_aliases))
23
+ select_append(*columns).clone(:graph_aliases => Hash[ga].merge!(graph_aliases).freeze)
24
24
  end
25
25
 
26
26
  # Similar to Dataset#join_table, but uses unambiguous aliases for selected
@@ -196,11 +196,12 @@ module Sequel
196
196
  ident = SQL::QualifiedIdentifier.new(table_alias_qualifier, column)
197
197
  [column, ident]
198
198
  end
199
- column_aliases[col_alias] = [table_alias, column]
199
+ column_aliases[col_alias] = [table_alias, column].freeze
200
200
  select.push(identifier)
201
201
  end
202
202
  end
203
- ds = ds.clone(:graph=>graph)
203
+ [:column_aliases, :table_aliases, :column_alias_num].each{|k| graph[k].freeze}
204
+ ds = ds.clone(:graph=>graph.freeze)
204
205
  add_columns ? ds.select(*select) : ds
205
206
  end
206
207
 
@@ -229,9 +230,7 @@ module Sequel
229
230
  # # SELECT artists.name, albums.name AS album_name, 42 AS forty_two ...
230
231
  def set_graph_aliases(graph_aliases)
231
232
  columns, graph_aliases = graph_alias_columns(graph_aliases)
232
- ds = select(*columns)
233
- ds.opts[:graph_aliases] = graph_aliases
234
- ds
233
+ select(*columns).clone(:graph_aliases=>graph_aliases.freeze)
235
234
  end
236
235
 
237
236
  # Remove the splitting of results into subhashes, and all metadata
@@ -269,7 +268,7 @@ module Sequel
269
268
  identifiers = graph_aliases.collect do |col_alias, tc|
270
269
  table, column, value = Array(tc)
271
270
  column ||= col_alias
272
- gas[col_alias] = [table, column]
271
+ gas[col_alias] = [table, column].freeze
273
272
  identifier = value || SQL::QualifiedIdentifier.new(table, column)
274
273
  identifier = SQL::AliasedExpression.new(identifier, col_alias) if value || column != col_alias
275
274
  identifier
@@ -29,13 +29,14 @@ module Sequel
29
29
  # the Database#dataset method return an instance of that subclass.
30
30
  def initialize(db)
31
31
  @db = db
32
- @opts = OPTS
32
+ @opts = {}
33
+ @cache = {}
33
34
  end
34
35
 
35
- # Define a hash value such that datasets with the same DB, opts, and SQL
36
+ # Define a hash value such that datasets with the same class, DB, and opts
36
37
  # will be considered equal.
37
38
  def ==(o)
38
- o.is_a?(self.class) && db == o.db && opts == o.opts && sql == o.sql
39
+ o.is_a?(self.class) && db == o.db && opts == o.opts
39
40
  end
40
41
 
41
42
  # An object representing the current date or time, should be an instance
@@ -49,11 +50,19 @@ module Sequel
49
50
  self == o
50
51
  end
51
52
 
52
- # Similar to #clone, but returns an unfrozen clone if the receiver is frozen.
53
- def dup
54
- o = clone
55
- o.instance_variable_set(:@frozen, false) if frozen?
56
- o
53
+ if TRUE_FREEZE
54
+ # Similar to #clone, but returns an unfrozen clone if the receiver is frozen.
55
+ def dup
56
+ _clone(:freeze=>false)
57
+ end
58
+ else
59
+ # :nocov:
60
+ def dup
61
+ c = clone
62
+ c.instance_variable_set(:@opts, Hash[c.opts])
63
+ c
64
+ end
65
+ # :nocov:
57
66
  end
58
67
 
59
68
  # Yield a dataset for each server in the connection pool that is tied to that server.
@@ -74,15 +83,25 @@ module Sequel
74
83
  string.gsub(/[\\%_]/){|m| "\\#{m}"}
75
84
  end
76
85
 
77
- # Sets the frozen flag on the dataset, so you can't modify it. Returns the receiver.
78
- def freeze
79
- @frozen = true
80
- self
81
- end
86
+ if TRUE_FREEZE
87
+ # Freeze the opts when freezing the dataset.
88
+ def freeze
89
+ @opts.freeze
90
+ super
91
+ end
92
+ else
93
+ # :nocov:
94
+ # :nodoc:
95
+ def freeze
96
+ @opts.freeze
97
+ self
98
+ end
82
99
 
83
- # Whether the object is frozen.
84
- def frozen?
85
- @frozen == true
100
+ # :nodoc:
101
+ def frozen?
102
+ @opts.frozen?
103
+ end
104
+ # :nocov:
86
105
  end
87
106
 
88
107
  # Alias of +first_source_alias+
@@ -139,30 +158,10 @@ module Sequel
139
158
  end
140
159
  end
141
160
 
142
- # Define a hash value such that datasets with the same DB, opts, and SQL
143
- # will have the same hash value
161
+ # Define a hash value such that datasets with the same class, DB, and opts,
162
+ # will have the same hash value.
144
163
  def hash
145
- [db, opts, sql].hash
146
- end
147
-
148
- # The String instance method to call on identifiers before sending them to
149
- # the database.
150
- def identifier_input_method
151
- if defined?(@identifier_input_method)
152
- @identifier_input_method
153
- else
154
- @identifier_input_method = db.identifier_input_method
155
- end
156
- end
157
-
158
- # The String instance method to call on identifiers before sending them to
159
- # the database.
160
- def identifier_output_method
161
- if defined?(@identifier_output_method)
162
- @identifier_output_method
163
- else
164
- @identifier_output_method = db.identifier_output_method
165
- end
164
+ [self.class, db, opts].hash
166
165
  end
167
166
 
168
167
  # Returns a string representation of the dataset including the class name
@@ -182,6 +181,12 @@ module Sequel
182
181
  :x_sequel_row_number_x
183
182
  end
184
183
 
184
+ # The row_proc for this database, should be any object that responds to +call+ with
185
+ # a single hash argument and returns the object you want #each to return.
186
+ def row_proc
187
+ @opts[:row_proc]
188
+ end
189
+
185
190
  # Splits a possible implicit alias in +c+, handling both SQL::AliasedExpressions
186
191
  # and Symbols. Returns an array of two elements, with the first being the
187
192
  # main expression, and the second being the alias.
@@ -250,29 +255,95 @@ module Sequel
250
255
 
251
256
  # Return a modified dataset with quote_identifiers set.
252
257
  def with_quote_identifiers(v)
253
- c = clone
254
- c.send(:skip_symbol_cache!)
255
- c.instance_variable_set(:@quote_identifiers, v)
256
- c
258
+ clone(:quote_identifiers=>v, :skip_symbol_cache=>true)
257
259
  end
258
-
259
- # Return a modified dataset with identifier_input_method set.
260
- def with_identifier_input_method(meth)
261
- c = clone
262
- c.send(:skip_symbol_cache!)
263
- c.instance_variable_set(:@identifier_input_method, meth)
264
- c
260
+
261
+ protected
262
+
263
+ # Access the cache for the current dataset. Should be used with caution,
264
+ # as access to the cache is not thread safe without a mutex if other
265
+ # threads can reference the dataset. Symbol keys prefixed with an
266
+ # underscore are reserved for internal use.
267
+ attr_reader :cache
268
+
269
+ # Retreive a value from the dataset's cache in a thread safe manner.
270
+ def cache_get(k)
271
+ Sequel.synchronize{@cache[k]}
265
272
  end
266
-
267
- # Return a modified dataset with identifier_output_method set.
268
- def with_identifier_output_method(meth)
269
- c = clone
270
- c.instance_variable_set(:@identifier_output_method, meth)
271
- c
273
+
274
+ # Set a value in the dataset's cache in a thread safe manner.
275
+ def cache_set(k, v)
276
+ Sequel.synchronize{@cache[k] = v}
277
+ end
278
+
279
+ # Clear the columns hash for the current dataset. This is not a
280
+ # thread safe operation, so it should only be used if the dataset
281
+ # could not be used by another thread (such as one that was just
282
+ # created via clone).
283
+ def clear_columns_cache
284
+ @cache.delete(:_columns)
285
+ end
286
+
287
+ # The cached columns for the current dataset.
288
+ def _columns
289
+ cache_get(:_columns)
272
290
  end
273
291
 
274
292
  private
275
293
 
294
+ # Check the cache for the given key, returning the value.
295
+ # Otherwise, yield to get the dataset, and if the current dataset
296
+ # is frozen, cache the dataset under the given key.
297
+ def cached_dataset(key)
298
+ unless ds = cache_get(key)
299
+ ds = yield
300
+ cache_set(key, ds) if frozen?
301
+ end
302
+
303
+ ds
304
+ end
305
+
306
+ # Return a cached placeholder literalizer for the given key if there
307
+ # is one for this dataset. If there isn't one, increment the counter
308
+ # for the number of calls for the key, and if the counter is at least
309
+ # three, then create a placeholder literalizer by yielding to the block,
310
+ # and cache it.
311
+ def cached_placeholder_literalizer(key)
312
+ if loader = cache_get(key)
313
+ return loader unless loader.is_a?(Integer)
314
+ loader += 1
315
+
316
+ if loader >= 3
317
+ loader = Sequel::Dataset::PlaceholderLiteralizer.loader(self){|pl, _| yield pl}
318
+ cache_set(key, loader)
319
+ else
320
+ cache_set(key, loader + 1)
321
+ loader = nil
322
+ end
323
+ elsif cache_sql?
324
+ cache_set(key, 1)
325
+ end
326
+
327
+ loader
328
+ end
329
+
330
+ # Set the columns for the current dataset.
331
+ def columns=(v)
332
+ cache_set(:_columns, v)
333
+ end
334
+
335
+ # Set the db, opts, and cache for the copy of the dataset.
336
+ def initialize_copy(c)
337
+ @db = c.db
338
+ @opts = Hash[c.opts]
339
+ if cols = c.cache_get(:_columns)
340
+ @cache = {:_columns=>cols}
341
+ else
342
+ @cache = {}
343
+ end
344
+ end
345
+ alias initialize_clone initialize_copy
346
+
276
347
  # Internal recursive version of unqualified_column_for, handling Strings inside
277
348
  # of other objects.
278
349
  def _unqualified_column_for(v)
@@ -28,10 +28,6 @@ module Sequel
28
28
  # Add the mutation methods via metaprogramming
29
29
  def_mutation_method(*MUTATION_METHODS)
30
30
 
31
- # The row_proc for this database, should be any object that responds to +call+ with
32
- # a single hash argument and returns the object you want #each to return.
33
- attr_reader :row_proc
34
-
35
31
  # Like #extension, but modifies and returns the receiver instead of returning a modified clone.
36
32
  def extension!(*exts)
37
33
  raise_if_frozen!
@@ -45,23 +41,10 @@ module Sequel
45
41
  self
46
42
  end
47
43
 
48
- # Set the method to call on identifiers going into the database for this dataset
49
- def identifier_input_method=(v)
50
- raise_if_frozen!
51
- skip_symbol_cache!
52
- @identifier_input_method = v
53
- end
54
-
55
- # Set the method to call on identifiers coming the database for this dataset
56
- def identifier_output_method=(v)
57
- raise_if_frozen!
58
- @identifier_output_method = v
59
- end
60
-
61
44
  # Remove the row_proc from the current dataset.
62
45
  def naked!
63
46
  raise_if_frozen!
64
- self.row_proc = nil
47
+ @opts[:row_proc] = nil
65
48
  self
66
49
  end
67
50
 
@@ -69,13 +52,13 @@ module Sequel
69
52
  def quote_identifiers=(v)
70
53
  raise_if_frozen!
71
54
  skip_symbol_cache!
72
- @quote_identifiers = v
55
+ @opts[:quote_identifiers] = v
73
56
  end
74
57
 
75
58
  # Override the row_proc for this dataset
76
59
  def row_proc=(v)
77
60
  raise_if_frozen!
78
- @row_proc = v
61
+ @opts[:row_proc] = v
79
62
  end
80
63
 
81
64
  private
@@ -99,6 +82,7 @@ module Sequel
99
82
  def mutation_method(meth, *args, &block)
100
83
  raise_if_frozen!
101
84
  @opts = send(meth, *args, &block).opts
85
+ @cache = {}
102
86
  self
103
87
  end
104
88
 
@@ -108,5 +92,10 @@ module Sequel
108
92
  raise RuntimeError, "can't modify frozen #{visible_class_name}"
109
93
  end
110
94
  end
95
+
96
+ # Set the dataset to skip the symbol cache
97
+ def skip_symbol_cache!
98
+ @opts[:skip_symbol_cache] = true
99
+ end
111
100
  end
112
101
  end
@@ -51,6 +51,7 @@ module Sequel
51
51
  @recorder = recorder
52
52
  @pos = pos
53
53
  @transformer = transformer
54
+ freeze
54
55
  end
55
56
 
56
57
  # Record the SQL query offset, argument position, and transforming block where the
@@ -125,13 +126,21 @@ module Sequel
125
126
  @fragments = fragments
126
127
  @final_sql = final_sql
127
128
  @arity = arity
129
+ freeze
130
+ end
131
+
132
+ # Freeze the fragments and final SQL when freezing the literalizer.
133
+ def freeze
134
+ @fragments.freeze
135
+ @final_sql.freeze
136
+ super
128
137
  end
129
138
 
130
139
  # Return a new PlaceholderLiteralizer with a modified dataset. This yields the
131
140
  # receiver's dataset to the block, and the block should return the new dataset
132
141
  # to use.
133
142
  def with_dataset
134
- dup.instance_exec{@dataset = yield @dataset; self}
143
+ dup.instance_exec{@dataset = yield @dataset; self}.freeze
135
144
  end
136
145
 
137
146
  # Return an array of all objects by running the SQL query for the given arguments.
@@ -36,33 +36,49 @@ module Sequel
36
36
  end
37
37
  end
38
38
  private_class_method :prepared_statements_module
39
+
40
+ def self.def_deprecated_opts_setter(mod, *meths)
41
+ meths.each do |meth|
42
+ mod.send(:define_method, :"#{meth}=") do |v|
43
+ # :nocov:
44
+ Sequel::Deprecation.deprecate("Dataset##{meth}=", "The API has changed, and this value should now be passed in as an option via Dataset#clone.")
45
+ @opts[meth] = v
46
+ # :nocov:
47
+ end
48
+ end
49
+ end
39
50
 
40
51
  # Default implementation of the argument mapper to allow
41
52
  # native database support for bind variables and prepared
42
53
  # statements (as opposed to the emulated ones used by default).
43
54
  module ArgumentMapper
44
- # The name of the prepared statement, if any.
45
- attr_accessor :prepared_statement_name
55
+ Dataset.def_deprecated_opts_setter(self, :prepared_statement_name, :bind_arguments)
46
56
 
57
+ # The name of the prepared statement, if any.
58
+ def prepared_statement_name
59
+ @opts[:prepared_statement_name]
60
+ end
61
+
47
62
  # The bind arguments to use for running this prepared statement
48
- attr_accessor :bind_arguments
63
+ def bind_arguments
64
+ @opts[:bind_arguments]
65
+ end
49
66
 
50
67
  # Set the bind arguments based on the hash and call super.
51
68
  def call(bind_vars={}, &block)
52
- ds = bind(bind_vars)
53
- ds.prepared_sql
54
- ds.bind_arguments = ds.map_to_prepared_args(ds.opts[:bind_vars])
55
- ds.run(&block)
69
+ sql = prepared_sql
70
+ prepared_args.freeze
71
+ ps = bind(bind_vars)
72
+ ps.clone(:bind_arguments=>ps.map_to_prepared_args(ps.opts[:bind_vars]), :sql=>sql, :prepared_sql=>sql).run(&block)
56
73
  end
57
74
 
58
75
  # Override the given *_sql method based on the type, and
59
76
  # cache the result of the sql.
60
77
  def prepared_sql
61
- return @prepared_sql if @prepared_sql
62
- @prepared_args ||= []
63
- @prepared_sql = super
64
- @opts[:sql] = @prepared_sql
65
- @prepared_sql
78
+ if sql = @opts[:prepared_sql] || cache_get(:_prepared_sql)
79
+ return sql
80
+ end
81
+ cache_set(:_prepared_sql, super)
66
82
  end
67
83
  end
68
84
 
@@ -73,25 +89,37 @@ module Sequel
73
89
  # into the query, which works on all databases, as it is no different
74
90
  # from using the dataset without bind variables.
75
91
  module PreparedStatementMethods
92
+ Dataset.def_deprecated_opts_setter(self, :log_sql, :prepared_type, :prepared_args, :orig_dataset, :prepared_modify_values)
93
+
76
94
  PLACEHOLDER_RE = /\A\$(.*)\z/
77
95
 
78
96
  # Whether to log the full SQL query. By default, just the prepared statement
79
97
  # name is generally logged on adapters that support native prepared statements.
80
- attr_accessor :log_sql
98
+ def log_sql
99
+ @opts[:log_sql]
100
+ end
81
101
 
82
102
  # The type of prepared statement, should be one of :select, :first,
83
103
  # :insert, :update, or :delete
84
- attr_accessor :prepared_type
104
+ def prepared_type
105
+ @opts[:prepared_type]
106
+ end
85
107
 
86
108
  # The array/hash of bound variable placeholder names.
87
- attr_accessor :prepared_args
109
+ def prepared_args
110
+ @opts[:prepared_args]
111
+ end
88
112
 
89
113
  # The dataset that created this prepared statement.
90
- attr_accessor :orig_dataset
114
+ def orig_dataset
115
+ @opts[:orig_dataset]
116
+ end
91
117
 
92
118
  # The argument to supply to insert and update, which may use
93
119
  # placeholders specified by prepared_args
94
- attr_accessor :prepared_modify_values
120
+ def prepared_modify_values
121
+ @opts[:prepared_modify_values]
122
+ end
95
123
 
96
124
  # Sets the prepared_args to the given hash and runs the
97
125
  # prepared statement.
@@ -115,18 +143,18 @@ module Sequel
115
143
  # Returns the SQL for the prepared statement, depending on
116
144
  # the type of the statement and the prepared_modify_values.
117
145
  def prepared_sql
118
- case @prepared_type
146
+ case prepared_type
119
147
  when :select, :all, :each
120
148
  # Most common scenario, so listed first.
121
149
  select_sql
122
150
  when :first
123
151
  clone(:limit=>1).select_sql
124
152
  when :insert_select
125
- insert_select_sql(*@prepared_modify_values)
126
- when :insert
127
- insert_sql(*@prepared_modify_values)
153
+ insert_select_sql(*prepared_modify_values)
154
+ when :insert, :insert_pk
155
+ insert_sql(*prepared_modify_values)
128
156
  when :update
129
- update_sql(*@prepared_modify_values)
157
+ update_sql(*prepared_modify_values)
130
158
  when :delete
131
159
  delete_sql
132
160
  else
@@ -163,7 +191,7 @@ module Sequel
163
191
  # :select running #all to get all of the rows, and the other
164
192
  # types running the method with the same name as the type.
165
193
  def run(&block)
166
- case @prepared_type
194
+ case prepared_type
167
195
  when :select, :all
168
196
  # Most common scenario, so listed first
169
197
  all(&block)
@@ -174,17 +202,19 @@ module Sequel
174
202
  when :first
175
203
  first
176
204
  when :insert, :update, :delete
177
- if opts[:returning] && supports_returning?(@prepared_type)
205
+ if opts[:returning] && supports_returning?(prepared_type)
178
206
  returning_fetch_rows(prepared_sql)
179
- elsif @prepared_type == :delete
207
+ elsif prepared_type == :delete
180
208
  delete
181
209
  else
182
- send(@prepared_type, *@prepared_modify_values)
210
+ send(prepared_type, *prepared_modify_values)
183
211
  end
212
+ when :insert_pk
213
+ fetch_rows(prepared_sql){|r| return r.values.first}
184
214
  when Array
185
- case @prepared_type.at(0)
215
+ case prepared_type.at(0)
186
216
  when :map, :to_hash, :to_hash_groups
187
- send(*@prepared_type, &block)
217
+ send(*prepared_type, &block)
188
218
  end
189
219
  else
190
220
  all(&block)
@@ -213,10 +243,9 @@ module Sequel
213
243
  # support and using the same argument hash so that you can use
214
244
  # bind variables/prepared arguments in subselects.
215
245
  def subselect_sql_append(sql, ds)
216
- ps = ds.clone(:append_sql=>sql).prepare(:select)
217
- ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
218
- ps.prepared_args = prepared_args
219
- ps.prepared_sql
246
+ ds.clone(:append_sql=>sql, :prepared_args=>prepared_args, :bind_vars=>@opts[:bind_vars]).
247
+ send(:to_prepared_statement, :select, nil, :extend=>prepared_statement_modules).
248
+ prepared_sql
220
249
  end
221
250
  end
222
251
 
@@ -255,22 +284,32 @@ module Sequel
255
284
  # already been set for this dataset, they are updated with the contents
256
285
  # of bind_vars.
257
286
  #
258
- # DB[:table].filter(:id=>:$id).bind(:id=>1).call(:first)
287
+ # DB[:table].where(:id=>:$id).bind(:id=>1).call(:first)
259
288
  # # SELECT * FROM table WHERE id = ? LIMIT 1 -- (1)
260
289
  # # => {:id=>1}
261
290
  def bind(bind_vars={})
262
- clone(:bind_vars=>@opts[:bind_vars] ? Hash[@opts[:bind_vars]].merge!(bind_vars) : bind_vars)
291
+ bind_vars = if bv = @opts[:bind_vars]
292
+ Hash[bv].merge!(bind_vars).freeze
293
+ else
294
+ if bind_vars.frozen?
295
+ bind_vars
296
+ else
297
+ Hash[bind_vars]
298
+ end
299
+ end
300
+
301
+ clone(:bind_vars=>bind_vars)
263
302
  end
264
303
 
265
304
  # For the given type (:select, :first, :insert, :insert_select, :update, or :delete),
266
305
  # run the sql with the bind variables specified in the hash. +values+ is a hash passed to
267
306
  # insert or update (if one of those types is used), which may contain placeholders.
268
307
  #
269
- # DB[:table].filter(:id=>:$id).call(:first, :id=>1)
308
+ # DB[:table].where(:id=>:$id).call(:first, :id=>1)
270
309
  # # SELECT * FROM table WHERE id = ? LIMIT 1 -- (1)
271
310
  # # => {:id=>1}
272
311
  def call(type, bind_variables={}, *values, &block)
273
- prepare(type, nil, *values).call(bind_variables, &block)
312
+ to_prepared_statement(type, values, :extend=>bound_variable_modules).call(bind_variables, &block)
274
313
  end
275
314
 
276
315
  # Prepare an SQL statement for later execution. Takes a type similar to #call,
@@ -284,7 +323,7 @@ module Sequel
284
323
  # the associated database, where it can be called by name.
285
324
  # The following usage is identical:
286
325
  #
287
- # ps = DB[:table].filter(:name=>:$name).prepare(:first, :select_by_name)
326
+ # ps = DB[:table].where(:name=>:$name).prepare(:first, :select_by_name)
288
327
  #
289
328
  # ps.call(:name=>'Blah')
290
329
  # # SELECT * FROM table WHERE name = ? -- ('Blah')
@@ -292,8 +331,17 @@ module Sequel
292
331
  #
293
332
  # DB.call(:select_by_name, :name=>'Blah') # Same thing
294
333
  def prepare(type, name=nil, *values)
295
- ps = to_prepared_statement(type, values)
296
- db.set_prepared_statement(name, ps) if name
334
+ ps = to_prepared_statement(type, values, :name=>name, :extend=>prepared_statement_modules)
335
+
336
+ if name
337
+ ps.prepared_sql
338
+ db.set_prepared_statement(name, ps)
339
+ else
340
+ # :nocov:
341
+ Sequel::Deprecation.deprecate("Dataset#prepare will change to requiring a name argument in Sequel 5, please update your code.") unless name
342
+ # :nocov:
343
+ end
344
+
297
345
  ps
298
346
  end
299
347
 
@@ -301,13 +349,13 @@ module Sequel
301
349
 
302
350
  # Return a cloned copy of the current dataset extended with
303
351
  # PreparedStatementMethods, setting the type and modify values.
304
- def to_prepared_statement(type, values=nil)
305
- ps = bind
306
- ps.extend(PreparedStatementMethods)
307
- ps.orig_dataset = self
308
- ps.prepared_type = type
309
- ps.prepared_modify_values = values
310
- ps
352
+ def to_prepared_statement(type, values=nil, opts=OPTS)
353
+ mods = opts[:extend] || []
354
+ mods += [PreparedStatementMethods]
355
+
356
+ bind.
357
+ clone(:prepared_statement_name=>opts[:name], :prepared_type=>type, :prepared_modify_values=>values, :orig_dataset=>self, :no_cache_sql=>true, :prepared_args=>@opts[:prepared_args]||[]).
358
+ with_extend(*mods)
311
359
  end
312
360
 
313
361
  private
@@ -317,6 +365,14 @@ module Sequel
317
365
  false
318
366
  end
319
367
 
368
+ def bound_variable_modules
369
+ prepared_statement_modules
370
+ end
371
+
372
+ def prepared_statement_modules
373
+ []
374
+ end
375
+
320
376
  # The argument placeholder. Most databases used unnumbered
321
377
  # arguments with question marks, so that is the default.
322
378
  def prepared_arg_placeholder