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