sequel 3.21.0 → 3.31.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 (225) hide show
  1. data/CHANGELOG +485 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +37 -19
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +249 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/dataset_filtering.rdoc +8 -0
  8. data/doc/mass_assignment.rdoc +54 -0
  9. data/doc/migration.rdoc +15 -538
  10. data/doc/model_hooks.rdoc +72 -27
  11. data/doc/opening_databases.rdoc +89 -50
  12. data/doc/prepared_statements.rdoc +40 -13
  13. data/doc/reflection.rdoc +8 -2
  14. data/doc/release_notes/3.22.0.txt +39 -0
  15. data/doc/release_notes/3.23.0.txt +172 -0
  16. data/doc/release_notes/3.24.0.txt +420 -0
  17. data/doc/release_notes/3.25.0.txt +88 -0
  18. data/doc/release_notes/3.26.0.txt +88 -0
  19. data/doc/release_notes/3.27.0.txt +82 -0
  20. data/doc/release_notes/3.28.0.txt +304 -0
  21. data/doc/release_notes/3.29.0.txt +459 -0
  22. data/doc/release_notes/3.30.0.txt +135 -0
  23. data/doc/release_notes/3.31.0.txt +146 -0
  24. data/doc/schema_modification.rdoc +547 -0
  25. data/doc/sharding.rdoc +7 -1
  26. data/doc/testing.rdoc +129 -0
  27. data/doc/transactions.rdoc +137 -0
  28. data/doc/virtual_rows.rdoc +2 -2
  29. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  30. data/lib/sequel/adapters/ado.rb +40 -18
  31. data/lib/sequel/adapters/amalgalite.rb +17 -9
  32. data/lib/sequel/adapters/db2.rb +175 -86
  33. data/lib/sequel/adapters/dbi.rb +15 -15
  34. data/lib/sequel/adapters/do/mysql.rb +5 -7
  35. data/lib/sequel/adapters/do/postgres.rb +0 -5
  36. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/do.rb +17 -36
  38. data/lib/sequel/adapters/firebird.rb +27 -208
  39. data/lib/sequel/adapters/ibmdb.rb +448 -0
  40. data/lib/sequel/adapters/informix.rb +6 -23
  41. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  42. data/lib/sequel/adapters/jdbc/db2.rb +45 -0
  43. data/lib/sequel/adapters/jdbc/derby.rb +247 -0
  44. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  45. data/lib/sequel/adapters/jdbc/h2.rb +56 -29
  46. data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
  47. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  48. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  49. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  50. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  51. data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
  52. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  53. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  54. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  55. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  56. data/lib/sequel/adapters/jdbc.rb +231 -78
  57. data/lib/sequel/adapters/mock.rb +335 -0
  58. data/lib/sequel/adapters/mysql.rb +82 -184
  59. data/lib/sequel/adapters/mysql2.rb +26 -25
  60. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  61. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  62. data/lib/sequel/adapters/odbc.rb +28 -9
  63. data/lib/sequel/adapters/openbase.rb +3 -5
  64. data/lib/sequel/adapters/oracle.rb +351 -53
  65. data/lib/sequel/adapters/postgres.rb +194 -33
  66. data/lib/sequel/adapters/shared/access.rb +32 -9
  67. data/lib/sequel/adapters/shared/db2.rb +321 -0
  68. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  69. data/lib/sequel/adapters/shared/informix.rb +53 -0
  70. data/lib/sequel/adapters/shared/mssql.rb +231 -106
  71. data/lib/sequel/adapters/shared/mysql.rb +181 -63
  72. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +231 -41
  74. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  75. data/lib/sequel/adapters/shared/progress.rb +4 -7
  76. data/lib/sequel/adapters/shared/sqlite.rb +201 -57
  77. data/lib/sequel/adapters/sqlite.rb +29 -21
  78. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  79. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  80. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  81. data/lib/sequel/adapters/swift.rb +5 -5
  82. data/lib/sequel/adapters/tinytds.rb +160 -19
  83. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  84. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  85. data/lib/sequel/ast_transformer.rb +190 -0
  86. data/lib/sequel/connection_pool/threaded.rb +3 -2
  87. data/lib/sequel/connection_pool.rb +1 -1
  88. data/lib/sequel/core.rb +46 -5
  89. data/lib/sequel/database/connecting.rb +5 -6
  90. data/lib/sequel/database/dataset.rb +4 -4
  91. data/lib/sequel/database/dataset_defaults.rb +59 -1
  92. data/lib/sequel/database/logging.rb +1 -1
  93. data/lib/sequel/database/misc.rb +104 -19
  94. data/lib/sequel/database/query.rb +153 -58
  95. data/lib/sequel/database/schema_generator.rb +17 -7
  96. data/lib/sequel/database/schema_methods.rb +59 -29
  97. data/lib/sequel/dataset/actions.rb +231 -76
  98. data/lib/sequel/dataset/features.rb +91 -8
  99. data/lib/sequel/dataset/graph.rb +38 -11
  100. data/lib/sequel/dataset/misc.rb +50 -23
  101. data/lib/sequel/dataset/mutation.rb +5 -6
  102. data/lib/sequel/dataset/prepared_statements.rb +48 -27
  103. data/lib/sequel/dataset/query.rb +236 -43
  104. data/lib/sequel/dataset/sql.rb +665 -253
  105. data/lib/sequel/dataset.rb +8 -0
  106. data/lib/sequel/exceptions.rb +4 -0
  107. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  108. data/lib/sequel/extensions/migration.rb +6 -4
  109. data/lib/sequel/extensions/named_timezones.rb +5 -0
  110. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  111. data/lib/sequel/extensions/to_dot.rb +95 -83
  112. data/lib/sequel/model/associations.rb +1020 -337
  113. data/lib/sequel/model/base.rb +414 -132
  114. data/lib/sequel/model/errors.rb +1 -1
  115. data/lib/sequel/model/exceptions.rb +5 -1
  116. data/lib/sequel/model.rb +15 -8
  117. data/lib/sequel/plugins/association_pks.rb +22 -4
  118. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  119. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  120. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  121. data/lib/sequel/plugins/force_encoding.rb +6 -6
  122. data/lib/sequel/plugins/identity_map.rb +114 -7
  123. data/lib/sequel/plugins/many_through_many.rb +65 -7
  124. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  125. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  126. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  127. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  128. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  129. data/lib/sequel/plugins/serialization.rb +88 -44
  130. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  131. data/lib/sequel/plugins/sharding.rb +21 -54
  132. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  133. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  134. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  135. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  136. data/lib/sequel/sql.rb +109 -52
  137. data/lib/sequel/timezones.rb +57 -38
  138. data/lib/sequel/version.rb +1 -1
  139. data/spec/adapters/db2_spec.rb +146 -0
  140. data/spec/adapters/mssql_spec.rb +111 -29
  141. data/spec/adapters/mysql_spec.rb +135 -123
  142. data/spec/adapters/oracle_spec.rb +49 -77
  143. data/spec/adapters/postgres_spec.rb +267 -78
  144. data/spec/adapters/spec_helper.rb +6 -5
  145. data/spec/adapters/sqlite_spec.rb +35 -21
  146. data/spec/core/connection_pool_spec.rb +71 -92
  147. data/spec/core/core_sql_spec.rb +21 -30
  148. data/spec/core/database_spec.rb +788 -512
  149. data/spec/core/dataset_spec.rb +1073 -1053
  150. data/spec/core/expression_filters_spec.rb +159 -42
  151. data/spec/core/mock_adapter_spec.rb +419 -0
  152. data/spec/core/object_graph_spec.rb +86 -114
  153. data/spec/core/schema_generator_spec.rb +3 -3
  154. data/spec/core/schema_spec.rb +298 -38
  155. data/spec/core/spec_helper.rb +6 -48
  156. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  157. data/spec/extensions/association_dependencies_spec.rb +2 -12
  158. data/spec/extensions/association_pks_spec.rb +66 -32
  159. data/spec/extensions/caching_spec.rb +23 -50
  160. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  161. data/spec/extensions/columns_introspection_spec.rb +91 -0
  162. data/spec/extensions/composition_spec.rb +18 -13
  163. data/spec/extensions/dataset_associations_spec.rb +199 -0
  164. data/spec/extensions/defaults_setter_spec.rb +64 -0
  165. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  166. data/spec/extensions/identity_map_spec.rb +114 -22
  167. data/spec/extensions/instance_filters_spec.rb +11 -21
  168. data/spec/extensions/instance_hooks_spec.rb +53 -0
  169. data/spec/extensions/json_serializer_spec.rb +4 -5
  170. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  171. data/spec/extensions/list_spec.rb +17 -39
  172. data/spec/extensions/many_through_many_spec.rb +283 -250
  173. data/spec/extensions/migration_spec.rb +35 -32
  174. data/spec/extensions/named_timezones_spec.rb +23 -3
  175. data/spec/extensions/nested_attributes_spec.rb +97 -88
  176. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  177. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  178. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  179. data/spec/extensions/prepared_statements_spec.rb +53 -0
  180. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  181. data/spec/extensions/pretty_table_spec.rb +1 -6
  182. data/spec/extensions/rcte_tree_spec.rb +41 -43
  183. data/spec/extensions/schema_dumper_spec.rb +5 -8
  184. data/spec/extensions/schema_spec.rb +13 -21
  185. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  186. data/spec/extensions/serialization_spec.rb +72 -43
  187. data/spec/extensions/sharding_spec.rb +66 -140
  188. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  189. data/spec/extensions/spec_helper.rb +13 -61
  190. data/spec/extensions/sql_expr_spec.rb +20 -60
  191. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  192. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  193. data/spec/extensions/timestamps_spec.rb +6 -6
  194. data/spec/extensions/to_dot_spec.rb +4 -7
  195. data/spec/extensions/touch_spec.rb +13 -14
  196. data/spec/extensions/tree_spec.rb +11 -26
  197. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  198. data/spec/extensions/update_primary_key_spec.rb +30 -24
  199. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  200. data/spec/extensions/validation_helpers_spec.rb +16 -35
  201. data/spec/extensions/xml_serializer_spec.rb +16 -4
  202. data/spec/integration/associations_test.rb +537 -21
  203. data/spec/integration/database_test.rb +4 -2
  204. data/spec/integration/dataset_test.rb +461 -65
  205. data/spec/integration/eager_loader_test.rb +19 -21
  206. data/spec/integration/model_test.rb +80 -1
  207. data/spec/integration/plugin_test.rb +304 -116
  208. data/spec/integration/prepared_statement_test.rb +212 -122
  209. data/spec/integration/schema_test.rb +161 -30
  210. data/spec/integration/spec_helper.rb +39 -30
  211. data/spec/integration/timezone_test.rb +38 -12
  212. data/spec/integration/transaction_test.rb +172 -5
  213. data/spec/integration/type_test.rb +17 -3
  214. data/spec/model/association_reflection_spec.rb +201 -7
  215. data/spec/model/associations_spec.rb +865 -662
  216. data/spec/model/base_spec.rb +275 -95
  217. data/spec/model/dataset_methods_spec.rb +7 -27
  218. data/spec/model/eager_loading_spec.rb +677 -676
  219. data/spec/model/hooks_spec.rb +309 -67
  220. data/spec/model/model_spec.rb +222 -168
  221. data/spec/model/plugins_spec.rb +24 -13
  222. data/spec/model/record_spec.rb +381 -218
  223. data/spec/model/spec_helper.rb +13 -71
  224. data/spec/model/validations_spec.rb +11 -0
  225. metadata +115 -73
@@ -5,97 +5,63 @@ rescue LoadError
5
5
  end
6
6
  raise(LoadError, "require 'mysql' did not define Mysql::CLIENT_MULTI_RESULTS!\n You are probably using the pure ruby mysql.rb driver,\n which Sequel does not support. You need to install\n the C based adapter, and make sure that the mysql.so\n file is loaded instead of the mysql.rb file.\n") unless defined?(Mysql::CLIENT_MULTI_RESULTS)
7
7
 
8
- Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
8
+ Sequel.require %w'shared/mysql_prepared_statements', 'adapters'
9
9
 
10
10
  module Sequel
11
11
  # Module for holding all MySQL-related classes and modules for Sequel.
12
12
  module MySQL
13
13
  TYPE_TRANSLATOR = tt = Class.new do
14
14
  def boolean(s) s.to_i != 0 end
15
- def blob(s) ::Sequel::SQL::Blob.new(s) end
16
15
  def integer(s) s.to_i end
17
16
  def float(s) s.to_f end
18
- def decimal(s) ::BigDecimal.new(s) end
19
- def date(s) ::Sequel.string_to_date(s) end
20
- def time(s) ::Sequel.string_to_time(s) end
21
- def timestamp(s) ::Sequel.database_to_application_timestamp(s) end
22
- def date_conv(s) ::Sequel::MySQL.convert_date_time(:string_to_date, s) end
23
- def time_conv(s) ::Sequel::MySQL.convert_date_time(:string_to_time, s) end
24
- def timestamp_conv(s) ::Sequel::MySQL.convert_date_time(:database_to_application_timestamp, s) end
25
17
  end.new
26
18
 
27
19
  # Hash with integer keys and callable values for converting MySQL types.
28
20
  MYSQL_TYPES = {}
29
21
  {
30
- [0, 246] => tt.method(:decimal),
31
- [2, 3, 8, 9, 13, 247, 248] => tt.method(:integer),
32
- [4, 5] => tt.method(:float),
33
- [249, 250, 251, 252] => tt.method(:blob)
22
+ [0, 246] => ::BigDecimal.method(:new),
23
+ [2, 3, 8, 9, 13, 247, 248] => tt.method(:integer),
24
+ [4, 5] => tt.method(:float),
25
+ [249, 250, 251, 252] => ::Sequel::SQL::Blob.method(:new)
34
26
  }.each do |k,v|
35
27
  k.each{|n| MYSQL_TYPES[n] = v}
36
28
  end
37
29
 
38
- # Modify the type translator used for the tinyint type based
39
- # on the value given.
40
- def self.convert_tinyint_to_bool=(v)
41
- MYSQL_TYPES[1] = TYPE_TRANSLATOR.method(v ? :boolean : :integer)
42
- @convert_tinyint_to_bool = v
43
- end
44
- self.convert_tinyint_to_bool = convert_tinyint_to_bool
45
-
46
30
  class << self
47
- # By default, Sequel raises an exception if in invalid date or time is used.
48
- # However, if this is set to nil or :nil, the adapter treats dates
49
- # like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
50
- # it returns the strings as is.
51
- attr_reader :convert_invalid_date_time
52
- end
53
-
54
- # Modify the type translators for the date, time, and timestamp types
55
- # depending on the value given.
56
- def self.convert_invalid_date_time=(v)
57
- MYSQL_TYPES[11] = TYPE_TRANSLATOR.method(v == false ? :time : :time_conv)
58
- m = TYPE_TRANSLATOR.method(v == false ? :date : :date_conv)
59
- [10, 14].each{|i| MYSQL_TYPES[i] = m}
60
- m = TYPE_TRANSLATOR.method(v == false ? :timestamp : :timestamp_conv)
61
- [7, 12].each{|i| MYSQL_TYPES[i] = m}
62
- @convert_invalid_date_time = v
31
+ # Whether to convert invalid date time values by default.
32
+ attr_accessor :convert_invalid_date_time
63
33
  end
64
34
  self.convert_invalid_date_time = false
65
35
 
66
- # If convert_invalid_date_time is nil, :nil, or :string and
67
- # the conversion raises an InvalidValue exception, return v
68
- # if :string and nil otherwise.
69
- def self.convert_date_time(meth, v)
70
- begin
71
- Sequel.send(meth, v)
72
- rescue InvalidValue
73
- case @convert_invalid_date_time
74
- when nil, :nil
75
- nil
76
- when :string
77
- v
78
- else
79
- raise
80
- end
81
- end
82
- end
83
-
84
36
  # Database class for MySQL databases used with Sequel.
85
37
  class Database < Sequel::Database
86
38
  include Sequel::MySQL::DatabaseMethods
39
+ include Sequel::MySQL::PreparedStatements::DatabaseMethods
87
40
 
88
41
  # Mysql::Error messages that indicate the current connection should be disconnected
89
42
  MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away|Lost connection to MySQL server during query)/
90
43
 
91
44
  set_adapter_scheme :mysql
45
+
46
+ # Hash of conversion procs for the current database
47
+ attr_reader :conversion_procs
48
+ #
49
+ # Whether to convert tinyint columns to bool for the current database
50
+ attr_reader :convert_tinyint_to_bool
51
+
52
+ # By default, Sequel raises an exception if in invalid date or time is used.
53
+ # However, if this is set to nil or :nil, the adapter treats dates
54
+ # like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
55
+ # it returns the strings as is.
56
+ attr_reader :convert_invalid_date_time
92
57
 
93
- # Support stored procedures on MySQL
94
- def call_sproc(name, opts={}, &block)
95
- args = opts[:args] || []
96
- execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
58
+ def initialize(opts={})
59
+ super
60
+ @conversion_procs = MYSQL_TYPES.dup
61
+ self.convert_tinyint_to_bool = Sequel::MySQL.convert_tinyint_to_bool
62
+ self.convert_invalid_date_time = Sequel::MySQL.convert_invalid_date_time
97
63
  end
98
-
64
+
99
65
  # Connect to the database. In addition to the usual database options,
100
66
  # the following options have effect:
101
67
  #
@@ -139,7 +105,7 @@ module Sequel
139
105
  opts[:user],
140
106
  opts[:password],
141
107
  opts[:database],
142
- opts[:port],
108
+ (opts[:port].to_i if opts[:port]),
143
109
  opts[:socket],
144
110
  Mysql::CLIENT_MULTI_RESULTS +
145
111
  Mysql::CLIENT_MULTI_STATEMENTS +
@@ -161,30 +127,31 @@ module Sequel
161
127
 
162
128
  sqls.each{|sql| log_yield(sql){conn.query(sql)}}
163
129
 
164
- class << conn
165
- attr_accessor :prepared_statements
166
- end
167
- conn.prepared_statements = {}
130
+ add_prepared_statements_cache(conn)
168
131
  conn
169
132
  end
170
133
 
171
- # Returns instance of Sequel::MySQL::Dataset with the given options.
172
- def dataset(opts = nil)
173
- MySQL::Dataset.new(self, opts)
134
+ # Modify the type translators for the date, time, and timestamp types
135
+ # depending on the value given.
136
+ def convert_invalid_date_time=(v)
137
+ m0 = ::Sequel.method(:string_to_time)
138
+ @conversion_procs[11] = (v != false) ? lambda{|v| convert_date_time(v, &m0)} : m0
139
+ m1 = ::Sequel.method(:string_to_date)
140
+ m = (v != false) ? lambda{|v| convert_date_time(v, &m1)} : m1
141
+ [10, 14].each{|i| @conversion_procs[i] = m}
142
+ m2 = method(:to_application_timestamp)
143
+ m = (v != false) ? lambda{|v| convert_date_time(v, &m2)} : m2
144
+ [7, 12].each{|i| @conversion_procs[i] = m}
145
+ @convert_invalid_date_time = v
174
146
  end
175
-
176
- # Executes the given SQL using an available connection, yielding the
177
- # connection if the block is given.
178
- def execute(sql, opts={}, &block)
179
- if opts[:sproc]
180
- call_sproc(sql, opts, &block)
181
- elsif sql.is_a?(Symbol)
182
- execute_prepared_statement(sql, opts, &block)
183
- else
184
- synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
185
- end
147
+
148
+ # Modify the type translator used for the tinyint type based
149
+ # on the value given.
150
+ def convert_tinyint_to_bool=(v)
151
+ @conversion_procs[1] = TYPE_TRANSLATOR.method(v ? :boolean : :integer)
152
+ @convert_tinyint_to_bool = v
186
153
  end
187
-
154
+
188
155
  # Return the version of the MySQL server two which we are connecting.
189
156
  def server_version(server=nil)
190
157
  @server_version ||= (synchronize(server){|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
@@ -220,7 +187,7 @@ module Sequel
220
187
  end
221
188
  end
222
189
  rescue Mysql::Error => e
223
- raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
190
+ raise_error(e)
224
191
  ensure
225
192
  r.free if r
226
193
  # Use up all results to avoid a commands out of sync message.
@@ -244,10 +211,34 @@ module Sequel
244
211
  :query
245
212
  end
246
213
 
214
+ # If convert_invalid_date_time is nil, :nil, or :string and
215
+ # the conversion raises an InvalidValue exception, return v
216
+ # if :string and nil otherwise.
217
+ def convert_date_time(v)
218
+ begin
219
+ yield v
220
+ rescue InvalidValue
221
+ case @convert_invalid_date_time
222
+ when nil, :nil
223
+ nil
224
+ when :string
225
+ v
226
+ else
227
+ raise
228
+ end
229
+ end
230
+ end
231
+
247
232
  # The MySQL adapter main error class is Mysql::Error
248
233
  def database_error_classes
249
234
  [Mysql::Error]
250
235
  end
236
+
237
+ # Raise a disconnect error if the exception message matches the list
238
+ # of recognized exceptions.
239
+ def disconnect_error?(e, opts)
240
+ super || (e.is_a?(::Mysql::Error) && MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
241
+ end
251
242
 
252
243
  # The database name when using the native adapter is always stored in
253
244
  # the :database option.
@@ -262,97 +253,18 @@ module Sequel
262
253
  nil
263
254
  end
264
255
 
265
- # Executes a prepared statement on an available connection. If the
266
- # prepared statement already exists for the connection and has the same
267
- # SQL, reuse it, otherwise, prepare the new statement. Because of the
268
- # usual MySQL stupidity, we are forced to name arguments via separate
269
- # SET queries. Use @sequel_arg_N (for N starting at 1) for these
270
- # arguments.
271
- def execute_prepared_statement(ps_name, opts, &block)
272
- args = opts[:arguments]
273
- ps = prepared_statements[ps_name]
274
- sql = ps.prepared_sql
275
- synchronize(opts[:server]) do |conn|
276
- unless conn.prepared_statements[ps_name] == sql
277
- conn.prepared_statements[ps_name] = sql
278
- _execute(conn, "PREPARE #{ps_name} FROM '#{::Mysql.quote(sql)}'", opts)
279
- end
280
- i = 0
281
- _execute(conn, "SET " + args.map {|arg| "@sequel_arg_#{i+=1} = #{literal(arg)}"}.join(", "), opts) unless args.empty?
282
- _execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
283
- end
284
- end
285
-
286
256
  # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
287
257
  def schema_column_type(db_type)
288
- Sequel::MySQL.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
258
+ convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
289
259
  end
290
260
  end
291
261
 
292
262
  # Dataset class for MySQL datasets accessed via the native driver.
293
263
  class Dataset < Sequel::Dataset
294
264
  include Sequel::MySQL::DatasetMethods
295
- include StoredProcedures
296
-
297
- # Methods to add to MySQL prepared statement calls without using a
298
- # real database prepared statement and bound variables.
299
- module CallableStatementMethods
300
- # Extend given dataset with this module so subselects inside subselects in
301
- # prepared statements work.
302
- def subselect_sql(ds)
303
- ps = ds.to_prepared_statement(:select)
304
- ps.extend(CallableStatementMethods)
305
- ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
306
- ps.prepared_args = prepared_args
307
- ps.prepared_sql
308
- end
309
- end
310
-
311
- # Methods for MySQL prepared statements using the native driver.
312
- module PreparedStatementMethods
313
- include Sequel::Dataset::UnnumberedArgumentMapper
314
-
315
- private
316
-
317
- # Execute the prepared statement with the bind arguments instead of
318
- # the given SQL.
319
- def execute(sql, opts={}, &block)
320
- super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
321
- end
322
-
323
- # Same as execute, explicit due to intricacies of alias and super.
324
- def execute_dui(sql, opts={}, &block)
325
- super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
326
- end
327
- end
328
-
329
- # Methods for MySQL stored procedures using the native driver.
330
- module StoredProcedureMethods
331
- include Sequel::Dataset::StoredProcedureMethods
332
-
333
- private
334
-
335
- # Execute the database stored procedure with the stored arguments.
336
- def execute(sql, opts={}, &block)
337
- super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
338
- end
339
-
340
- # Same as execute, explicit due to intricacies of alias and super.
341
- def execute_dui(sql, opts={}, &block)
342
- super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
343
- end
344
- end
345
-
346
- # MySQL is different in that it supports prepared statements but not bound
347
- # variables outside of prepared statements. The default implementation
348
- # breaks the use of subselects in prepared statements, so extend the
349
- # temporary prepared statement that this creates with a module that
350
- # fixes it.
351
- def call(type, bind_arguments={}, *values, &block)
352
- ps = to_prepared_statement(type, values)
353
- ps.extend(CallableStatementMethods)
354
- ps.call(bind_arguments, &block)
355
- end
265
+ include Sequel::MySQL::PreparedStatements::DatasetMethods
266
+
267
+ Database::DatasetClass = self
356
268
 
357
269
  # Delete rows matching this dataset
358
270
  def delete
@@ -365,11 +277,12 @@ module Sequel
365
277
  def fetch_rows(sql, &block)
366
278
  execute(sql) do |r|
367
279
  i = -1
280
+ cps = db.conversion_procs
368
281
  cols = r.fetch_fields.map do |f|
369
282
  # Pretend tinyint is another integer type if its length is not 1, to
370
283
  # avoid casting to boolean if Sequel::MySQL.convert_tinyint_to_bool
371
284
  # is set.
372
- type_proc = f.type == 1 && f.length != 1 ? MYSQL_TYPES[2] : MYSQL_TYPES[f.type]
285
+ type_proc = f.type == 1 && f.length != 1 ? cps[2] : cps[f.type]
373
286
  [output_identifier(f.name), type_proc, i+=1]
374
287
  end
375
288
  @columns = cols.map{|c| c.first}
@@ -395,18 +308,6 @@ module Sequel
395
308
  execute_dui(insert_sql(*values)){|c| return c.insert_id}
396
309
  end
397
310
 
398
- # Store the given type of prepared statement in the associated database
399
- # with the given name.
400
- def prepare(type, name=nil, *values)
401
- ps = to_prepared_statement(type, values)
402
- ps.extend(PreparedStatementMethods)
403
- if name
404
- ps.prepared_statement_name = name
405
- db.prepared_statements[name] = ps
406
- end
407
- ps
408
- end
409
-
410
311
  # Replace (update or insert) the matching row.
411
312
  def replace(*args)
412
313
  execute_dui(replace_sql(*args)){|c| return c.insert_id}
@@ -446,13 +347,10 @@ module Sequel
446
347
  end
447
348
 
448
349
  # Handle correct quoting of strings using ::MySQL.quote.
449
- def literal_string(v)
450
- "'#{::Mysql.quote(v)}'"
451
- end
452
-
453
- # Extend the dataset with the MySQL stored procedure methods.
454
- def prepare_extend_sproc(ds)
455
- ds.extend(StoredProcedureMethods)
350
+ def literal_string_append(sql, v)
351
+ sql << "'"
352
+ sql << ::Mysql.quote(v)
353
+ sql << "'"
456
354
  end
457
355
 
458
356
  # Yield each row of the given result set r with columns cols
@@ -1,6 +1,5 @@
1
1
  require 'mysql2' unless defined? Mysql2
2
-
3
- Sequel.require %w'shared/mysql', 'adapters'
2
+ Sequel.require %w'shared/mysql_prepared_statements', 'adapters'
4
3
 
5
4
  module Sequel
6
5
  # Module for holding all Mysql2-related classes and modules for Sequel.
@@ -8,11 +7,21 @@ module Sequel
8
7
  # Database class for MySQL databases used with Sequel.
9
8
  class Database < Sequel::Database
10
9
  include Sequel::MySQL::DatabaseMethods
10
+ include Sequel::MySQL::PreparedStatements::DatabaseMethods
11
11
 
12
12
  # Mysql::Error messages that indicate the current connection should be disconnected
13
- MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away)/
13
+ MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away|This connection is still waiting for a result, try again once you have the result)/
14
14
 
15
15
  set_adapter_scheme :mysql2
16
+
17
+ # Whether to convert tinyint columns to bool for this database
18
+ attr_accessor :convert_tinyint_to_bool
19
+
20
+ # Set the convert_tinyint_to_bool setting based on the default value.
21
+ def initialize(opts={})
22
+ super
23
+ self.convert_tinyint_to_bool = Sequel::MySQL.convert_tinyint_to_bool
24
+ end
16
25
 
17
26
  # Connect to the database. In addition to the usual database options,
18
27
  # the following options have effect:
@@ -21,7 +30,6 @@ module Sequel
21
30
  # a filter for an autoincrement column equals NULL to return the last
22
31
  # inserted row.
23
32
  # * :charset - Same as :encoding (:encoding takes precendence)
24
- # * :compress - Set to false to not compress results from the server
25
33
  # * :config_default_group - The default group to read from the in
26
34
  # the MySQL config file.
27
35
  # * :config_local_infile - If provided, sets the Mysql::OPT_LOCAL_INFILE
@@ -57,24 +65,10 @@ module Sequel
57
65
 
58
66
  sqls.each{|sql| log_yield(sql){conn.query(sql)}}
59
67
 
68
+ add_prepared_statements_cache(conn)
60
69
  conn
61
70
  end
62
71
 
63
- # Returns instance of Sequel::MySQL::Dataset with the given options.
64
- def dataset(opts = nil)
65
- Mysql2::Dataset.new(self, opts)
66
- end
67
-
68
- # Executes the given SQL using an available connection, yielding the
69
- # connection if the block is given.
70
- def execute(sql, opts={}, &block)
71
- if opts[:sproc]
72
- call_sproc(sql, opts, &block)
73
- else
74
- synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
75
- end
76
- end
77
-
78
72
  # Return the version of the MySQL server two which we are connecting.
79
73
  def server_version(server=nil)
80
74
  @server_version ||= (synchronize(server){|conn| conn.server_info[:id]} || super)
@@ -87,14 +81,14 @@ module Sequel
87
81
  # yield the connection if a block is given.
88
82
  def _execute(conn, sql, opts)
89
83
  begin
90
- r = log_yield(sql){conn.query(sql, :symbolize_keys => true, :database_timezone => Sequel.database_timezone, :application_timezone => Sequel.application_timezone)}
84
+ r = log_yield(sql){conn.query(sql, :symbolize_keys => true, :database_timezone => timezone, :application_timezone => Sequel.application_timezone)}
91
85
  if opts[:type] == :select
92
86
  yield r if r
93
87
  elsif block_given?
94
88
  yield conn
95
89
  end
96
90
  rescue ::Mysql2::Error => e
97
- raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
91
+ raise_error(e)
98
92
  end
99
93
  end
100
94
 
@@ -108,6 +102,10 @@ module Sequel
108
102
  [::Mysql2::Error]
109
103
  end
110
104
 
105
+ def disconnect_error?(e, opts)
106
+ super || (e.is_a?(::Mysql2::Error) && MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
107
+ end
108
+
111
109
  # The database name when using the native adapter is always stored in
112
110
  # the :database option.
113
111
  def database_name
@@ -121,13 +119,16 @@ module Sequel
121
119
 
122
120
  # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
123
121
  def schema_column_type(db_type)
124
- Sequel::MySQL.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
122
+ convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
125
123
  end
126
124
  end
127
125
 
128
126
  # Dataset class for MySQL datasets accessed via the native driver.
129
127
  class Dataset < Sequel::Dataset
130
128
  include Sequel::MySQL::DatasetMethods
129
+ include Sequel::MySQL::PreparedStatements::DatasetMethods
130
+
131
+ Database::DatasetClass = self
131
132
 
132
133
  # Delete rows matching this dataset
133
134
  def delete
@@ -138,7 +139,7 @@ module Sequel
138
139
  def fetch_rows(sql, &block)
139
140
  execute(sql) do |r|
140
141
  @columns = r.fields
141
- r.each(:cast_booleans => Sequel::MySQL.convert_tinyint_to_bool, &block)
142
+ r.each(:cast_booleans => db.convert_tinyint_to_bool, &block)
142
143
  end
143
144
  self
144
145
  end
@@ -171,8 +172,8 @@ module Sequel
171
172
  end
172
173
 
173
174
  # Handle correct quoting of strings using ::Mysql2::Client#escape.
174
- def literal_string(v)
175
- db.synchronize{|c| "'#{c.escape(v)}'"}
175
+ def literal_string_append(sql, v)
176
+ sql << "'" << db.synchronize{|c| c.escape(v)} << "'"
176
177
  end
177
178
  end
178
179
  end
@@ -0,0 +1,17 @@
1
+ Sequel.require 'adapters/shared/db2'
2
+
3
+ module Sequel
4
+ module ODBC
5
+ # Database and Dataset instance methods for DB2 specific
6
+ # support via ODBC.
7
+ module DB2
8
+ module DatabaseMethods
9
+ include ::Sequel::DB2::DatabaseMethods
10
+ end
11
+
12
+ class Dataset < ODBC::Dataset
13
+ include ::Sequel::DB2::DatasetMethods
14
+ end
15
+ end
16
+ end
17
+ end
@@ -7,12 +7,7 @@ module Sequel
7
7
  module MSSQL
8
8
  module DatabaseMethods
9
9
  include Sequel::MSSQL::DatabaseMethods
10
- LAST_INSERT_ID_SQL='SELECT SCOPE_IDENTITY()'
11
-
12
- # Return an instance of Sequel::ODBC::MSSQL::Dataset with the given opts.
13
- def dataset(opts=nil)
14
- Sequel::ODBC::MSSQL::Dataset.new(self, opts)
15
- end
10
+ LAST_INSERT_ID_SQL='SELECT SCOPE_IDENTITY()'.freeze
16
11
 
17
12
  # Return the last inserted identity value.
18
13
  def execute_insert(sql, opts={})
@@ -15,9 +15,16 @@ module Sequel
15
15
  when 'mssql'
16
16
  Sequel.ts_require 'adapters/odbc/mssql'
17
17
  extend Sequel::ODBC::MSSQL::DatabaseMethods
18
+ @dataset_class = Sequel::ODBC::MSSQL::Dataset
19
+ set_mssql_unicode_strings
18
20
  when 'progress'
19
21
  Sequel.ts_require 'adapters/shared/progress'
20
22
  extend Sequel::Progress::DatabaseMethods
23
+ extend_datasets(Sequel::Progress::DatasetMethods)
24
+ when 'db2'
25
+ Sequel.ts_require 'adapters/odbc/db2'
26
+ extend Sequel::ODBC::DB2::DatabaseMethods
27
+ @dataset_class = Sequel::ODBC::DB2::Dataset
21
28
  end
22
29
  end
23
30
 
@@ -41,17 +48,13 @@ module Sequel
41
48
  conn
42
49
  end
43
50
 
44
- def dataset(opts = nil)
45
- ODBC::Dataset.new(self, opts)
46
- end
47
-
48
51
  def execute(sql, opts={})
49
52
  synchronize(opts[:server]) do |conn|
50
53
  begin
51
54
  r = log_yield(sql){conn.run(sql)}
52
55
  yield(r) if block_given?
53
56
  rescue ::ODBC::Error, ArgumentError => e
54
- raise_error(e, :disconnect=>DISCONNECT_ERRORS.match(e.message))
57
+ raise_error(e)
55
58
  ensure
56
59
  r.drop if r
57
60
  end
@@ -64,7 +67,7 @@ module Sequel
64
67
  begin
65
68
  log_yield(sql){conn.do(sql)}
66
69
  rescue ::ODBC::Error, ArgumentError => e
67
- raise_error(e, :disconnect=>DISCONNECT_ERRORS.match(e.message))
70
+ raise_error(e)
68
71
  end
69
72
  end
70
73
  end
@@ -76,9 +79,17 @@ module Sequel
76
79
  :do
77
80
  end
78
81
 
82
+ def database_error_classes
83
+ [::ODBC::Error]
84
+ end
85
+
79
86
  def disconnect_connection(c)
80
87
  c.disconnect
81
88
  end
89
+
90
+ def disconnect_error?(e, opts)
91
+ super || (e.is_a?(::ODBC::Error) && DISCONNECT_ERRORS.match(e.message))
92
+ end
82
93
  end
83
94
 
84
95
  class Dataset < Sequel::Dataset
@@ -87,15 +98,23 @@ module Sequel
87
98
  ODBC_DATE_FORMAT = "{d '%Y-%m-%d'}".freeze
88
99
  TIMESTAMP_FORMAT="{ts '%Y-%m-%d %H:%M:%S'}".freeze
89
100
 
101
+ Database::DatasetClass = self
102
+
90
103
  def fetch_rows(sql)
91
104
  execute(sql) do |s|
92
105
  i = -1
93
106
  cols = s.columns(true).map{|c| [output_identifier(c.name), i+=1]}
94
- @columns = cols.map{|c| c.at(0)}
107
+ columns = cols.map{|c| c.at(0)}
108
+ if opts[:offset] && offset_returns_row_number_column?
109
+ rn = row_number_column
110
+ columns.delete(rn)
111
+ end
112
+ @columns = columns
95
113
  if rows = s.fetch_all
96
114
  rows.each do |row|
97
115
  hash = {}
98
116
  cols.each{|n,i| hash[n] = convert_odbc_value(row[i])}
117
+ hash.delete(rn) if rn
99
118
  yield hash
100
119
  end
101
120
  end
@@ -114,9 +133,9 @@ module Sequel
114
133
  # ODBCColumn#mapSqlTypeToGenericType and Column#klass.
115
134
  case v
116
135
  when ::ODBC::TimeStamp
117
- Sequel.database_to_application_timestamp([v.year, v.month, v.day, v.hour, v.minute, v.second])
136
+ db.to_application_timestamp([v.year, v.month, v.day, v.hour, v.minute, v.second])
118
137
  when ::ODBC::Time
119
- Sequel.database_to_application_timestamp([now.year, now.month, now.day, v.hour, v.minute, v.second])
138
+ Sequel::SQLTime.create(v.hour, v.minute, v.second)
120
139
  when ::ODBC::Date
121
140
  Date.new(v.year, v.month, v.day)
122
141
  else
@@ -15,10 +15,6 @@ module Sequel
15
15
  )
16
16
  end
17
17
 
18
- def dataset(opts = nil)
19
- OpenBase::Dataset.new(self, opts)
20
- end
21
-
22
18
  def execute(sql, opts={})
23
19
  synchronize(opts[:server]) do |conn|
24
20
  r = log_yield(sql){conn.execute(sql)}
@@ -36,7 +32,9 @@ module Sequel
36
32
  end
37
33
 
38
34
  class Dataset < Sequel::Dataset
39
- SELECT_CLAUSE_METHODS = clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
35
+ SELECT_CLAUSE_METHODS = clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
36
+
37
+ Database::DatasetClass = self
40
38
 
41
39
  def fetch_rows(sql)
42
40
  execute(sql) do |result|