sequel 3.21.0 → 3.32.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 (234) hide show
  1. data/CHANGELOG +539 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +20 -6
  4. data/Rakefile +37 -19
  5. data/bin/sequel +1 -5
  6. data/doc/advanced_associations.rdoc +17 -0
  7. data/doc/association_basics.rdoc +293 -43
  8. data/doc/dataset_basics.rdoc +4 -4
  9. data/doc/dataset_filtering.rdoc +8 -0
  10. data/doc/mass_assignment.rdoc +54 -0
  11. data/doc/migration.rdoc +15 -538
  12. data/doc/model_hooks.rdoc +72 -27
  13. data/doc/opening_databases.rdoc +89 -50
  14. data/doc/prepared_statements.rdoc +40 -13
  15. data/doc/reflection.rdoc +8 -2
  16. data/doc/release_notes/3.22.0.txt +39 -0
  17. data/doc/release_notes/3.23.0.txt +172 -0
  18. data/doc/release_notes/3.24.0.txt +420 -0
  19. data/doc/release_notes/3.25.0.txt +88 -0
  20. data/doc/release_notes/3.26.0.txt +88 -0
  21. data/doc/release_notes/3.27.0.txt +82 -0
  22. data/doc/release_notes/3.28.0.txt +304 -0
  23. data/doc/release_notes/3.29.0.txt +459 -0
  24. data/doc/release_notes/3.30.0.txt +135 -0
  25. data/doc/release_notes/3.31.0.txt +146 -0
  26. data/doc/release_notes/3.32.0.txt +202 -0
  27. data/doc/schema_modification.rdoc +547 -0
  28. data/doc/sharding.rdoc +7 -1
  29. data/doc/testing.rdoc +129 -0
  30. data/doc/transactions.rdoc +137 -0
  31. data/doc/virtual_rows.rdoc +2 -2
  32. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  33. data/lib/sequel/adapters/ado.rb +40 -18
  34. data/lib/sequel/adapters/amalgalite.rb +17 -9
  35. data/lib/sequel/adapters/db2.rb +175 -86
  36. data/lib/sequel/adapters/dbi.rb +15 -15
  37. data/lib/sequel/adapters/do/mysql.rb +5 -7
  38. data/lib/sequel/adapters/do/postgres.rb +0 -5
  39. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  40. data/lib/sequel/adapters/do.rb +17 -36
  41. data/lib/sequel/adapters/firebird.rb +27 -208
  42. data/lib/sequel/adapters/ibmdb.rb +448 -0
  43. data/lib/sequel/adapters/informix.rb +6 -23
  44. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  45. data/lib/sequel/adapters/jdbc/db2.rb +52 -0
  46. data/lib/sequel/adapters/jdbc/derby.rb +260 -0
  47. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  48. data/lib/sequel/adapters/jdbc/h2.rb +66 -30
  49. data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
  50. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  51. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  52. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  53. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  54. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  56. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  57. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  58. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  59. data/lib/sequel/adapters/jdbc.rb +231 -78
  60. data/lib/sequel/adapters/mock.rb +339 -0
  61. data/lib/sequel/adapters/mysql.rb +85 -184
  62. data/lib/sequel/adapters/mysql2.rb +26 -25
  63. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  64. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  65. data/lib/sequel/adapters/odbc.rb +28 -9
  66. data/lib/sequel/adapters/openbase.rb +3 -5
  67. data/lib/sequel/adapters/oracle.rb +355 -53
  68. data/lib/sequel/adapters/postgres.rb +194 -33
  69. data/lib/sequel/adapters/shared/access.rb +32 -9
  70. data/lib/sequel/adapters/shared/db2.rb +328 -0
  71. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  72. data/lib/sequel/adapters/shared/informix.rb +53 -0
  73. data/lib/sequel/adapters/shared/mssql.rb +278 -107
  74. data/lib/sequel/adapters/shared/mysql.rb +203 -65
  75. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  76. data/lib/sequel/adapters/shared/oracle.rb +237 -46
  77. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  78. data/lib/sequel/adapters/shared/progress.rb +5 -8
  79. data/lib/sequel/adapters/shared/sqlite.rb +200 -50
  80. data/lib/sequel/adapters/sqlite.rb +29 -21
  81. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  82. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  83. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  84. data/lib/sequel/adapters/swift.rb +5 -5
  85. data/lib/sequel/adapters/tinytds.rb +160 -19
  86. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  87. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  88. data/lib/sequel/ast_transformer.rb +190 -0
  89. data/lib/sequel/connection_pool/threaded.rb +3 -2
  90. data/lib/sequel/connection_pool.rb +1 -1
  91. data/lib/sequel/core.rb +68 -5
  92. data/lib/sequel/database/connecting.rb +5 -6
  93. data/lib/sequel/database/dataset.rb +4 -4
  94. data/lib/sequel/database/dataset_defaults.rb +59 -1
  95. data/lib/sequel/database/logging.rb +1 -1
  96. data/lib/sequel/database/misc.rb +104 -19
  97. data/lib/sequel/database/query.rb +163 -59
  98. data/lib/sequel/database/schema_generator.rb +17 -7
  99. data/lib/sequel/database/schema_methods.rb +59 -29
  100. data/lib/sequel/dataset/actions.rb +240 -76
  101. data/lib/sequel/dataset/features.rb +91 -8
  102. data/lib/sequel/dataset/graph.rb +38 -11
  103. data/lib/sequel/dataset/misc.rb +50 -23
  104. data/lib/sequel/dataset/mutation.rb +12 -7
  105. data/lib/sequel/dataset/prepared_statements.rb +59 -27
  106. data/lib/sequel/dataset/query.rb +236 -43
  107. data/lib/sequel/dataset/sql.rb +676 -267
  108. data/lib/sequel/dataset.rb +8 -0
  109. data/lib/sequel/exceptions.rb +4 -0
  110. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  111. data/lib/sequel/extensions/migration.rb +6 -4
  112. data/lib/sequel/extensions/named_timezones.rb +5 -0
  113. data/lib/sequel/extensions/query.rb +1 -1
  114. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  115. data/lib/sequel/extensions/to_dot.rb +95 -83
  116. data/lib/sequel/model/associations.rb +1081 -344
  117. data/lib/sequel/model/base.rb +420 -133
  118. data/lib/sequel/model/errors.rb +1 -1
  119. data/lib/sequel/model/exceptions.rb +5 -1
  120. data/lib/sequel/model.rb +20 -10
  121. data/lib/sequel/plugins/active_model.rb +13 -1
  122. data/lib/sequel/plugins/association_pks.rb +22 -4
  123. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  124. data/lib/sequel/plugins/composition.rb +43 -10
  125. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  126. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  127. data/lib/sequel/plugins/force_encoding.rb +6 -6
  128. data/lib/sequel/plugins/identity_map.rb +114 -7
  129. data/lib/sequel/plugins/many_through_many.rb +68 -7
  130. data/lib/sequel/plugins/nested_attributes.rb +65 -10
  131. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  132. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  133. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  134. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  135. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  136. data/lib/sequel/plugins/serialization.rb +98 -49
  137. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  138. data/lib/sequel/plugins/sharding.rb +21 -54
  139. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  140. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  141. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  142. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  143. data/lib/sequel/sql.rb +109 -52
  144. data/lib/sequel/timezones.rb +57 -38
  145. data/lib/sequel/version.rb +1 -1
  146. data/spec/adapters/db2_spec.rb +146 -0
  147. data/spec/adapters/mssql_spec.rb +135 -30
  148. data/spec/adapters/mysql_spec.rb +244 -212
  149. data/spec/adapters/oracle_spec.rb +67 -77
  150. data/spec/adapters/postgres_spec.rb +267 -78
  151. data/spec/adapters/spec_helper.rb +6 -5
  152. data/spec/adapters/sqlite_spec.rb +35 -21
  153. data/spec/core/connection_pool_spec.rb +71 -92
  154. data/spec/core/core_sql_spec.rb +21 -30
  155. data/spec/core/database_spec.rb +789 -512
  156. data/spec/core/dataset_spec.rb +1176 -1061
  157. data/spec/core/expression_filters_spec.rb +159 -42
  158. data/spec/core/mock_adapter_spec.rb +419 -0
  159. data/spec/core/object_graph_spec.rb +86 -114
  160. data/spec/core/schema_generator_spec.rb +3 -3
  161. data/spec/core/schema_spec.rb +298 -38
  162. data/spec/core/spec_helper.rb +6 -48
  163. data/spec/extensions/active_model_spec.rb +13 -0
  164. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  165. data/spec/extensions/association_dependencies_spec.rb +2 -12
  166. data/spec/extensions/association_pks_spec.rb +66 -32
  167. data/spec/extensions/caching_spec.rb +23 -50
  168. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  169. data/spec/extensions/columns_introspection_spec.rb +91 -0
  170. data/spec/extensions/composition_spec.rb +18 -13
  171. data/spec/extensions/dataset_associations_spec.rb +199 -0
  172. data/spec/extensions/defaults_setter_spec.rb +64 -0
  173. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  174. data/spec/extensions/identity_map_spec.rb +114 -22
  175. data/spec/extensions/instance_filters_spec.rb +11 -21
  176. data/spec/extensions/instance_hooks_spec.rb +53 -0
  177. data/spec/extensions/json_serializer_spec.rb +4 -5
  178. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  179. data/spec/extensions/list_spec.rb +17 -39
  180. data/spec/extensions/many_through_many_spec.rb +283 -250
  181. data/spec/extensions/migration_spec.rb +35 -32
  182. data/spec/extensions/named_timezones_spec.rb +23 -3
  183. data/spec/extensions/nested_attributes_spec.rb +97 -88
  184. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  185. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  186. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  187. data/spec/extensions/prepared_statements_spec.rb +53 -0
  188. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  189. data/spec/extensions/pretty_table_spec.rb +1 -6
  190. data/spec/extensions/query_spec.rb +6 -0
  191. data/spec/extensions/rcte_tree_spec.rb +41 -43
  192. data/spec/extensions/schema_dumper_spec.rb +5 -8
  193. data/spec/extensions/schema_spec.rb +13 -21
  194. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  195. data/spec/extensions/serialization_spec.rb +81 -43
  196. data/spec/extensions/sharding_spec.rb +66 -140
  197. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  198. data/spec/extensions/spec_helper.rb +13 -61
  199. data/spec/extensions/sql_expr_spec.rb +20 -60
  200. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  201. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  202. data/spec/extensions/timestamps_spec.rb +6 -6
  203. data/spec/extensions/to_dot_spec.rb +4 -7
  204. data/spec/extensions/touch_spec.rb +13 -14
  205. data/spec/extensions/tree_spec.rb +11 -26
  206. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  207. data/spec/extensions/update_primary_key_spec.rb +30 -24
  208. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  209. data/spec/extensions/validation_helpers_spec.rb +16 -35
  210. data/spec/extensions/xml_serializer_spec.rb +16 -4
  211. data/spec/integration/associations_test.rb +661 -21
  212. data/spec/integration/database_test.rb +4 -2
  213. data/spec/integration/dataset_test.rb +500 -67
  214. data/spec/integration/eager_loader_test.rb +19 -21
  215. data/spec/integration/model_test.rb +80 -1
  216. data/spec/integration/plugin_test.rb +367 -119
  217. data/spec/integration/prepared_statement_test.rb +222 -125
  218. data/spec/integration/schema_test.rb +217 -39
  219. data/spec/integration/spec_helper.rb +39 -30
  220. data/spec/integration/timezone_test.rb +38 -12
  221. data/spec/integration/transaction_test.rb +182 -5
  222. data/spec/integration/type_test.rb +17 -3
  223. data/spec/model/association_reflection_spec.rb +201 -7
  224. data/spec/model/associations_spec.rb +955 -662
  225. data/spec/model/base_spec.rb +275 -95
  226. data/spec/model/dataset_methods_spec.rb +7 -27
  227. data/spec/model/eager_loading_spec.rb +677 -676
  228. data/spec/model/hooks_spec.rb +349 -67
  229. data/spec/model/model_spec.rb +222 -168
  230. data/spec/model/plugins_spec.rb +24 -13
  231. data/spec/model/record_spec.rb +381 -218
  232. data/spec/model/spec_helper.rb +13 -71
  233. data/spec/model/validations_spec.rb +11 -0
  234. metadata +117 -73
@@ -5,97 +5,66 @@ 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
+ #
33
+ # Only applies to Sequel::Database instances created after this
34
+ # has been set.
35
+ attr_accessor :convert_invalid_date_time
63
36
  end
64
37
  self.convert_invalid_date_time = false
65
38
 
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
39
  # Database class for MySQL databases used with Sequel.
85
40
  class Database < Sequel::Database
86
41
  include Sequel::MySQL::DatabaseMethods
42
+ include Sequel::MySQL::PreparedStatements::DatabaseMethods
87
43
 
88
44
  # Mysql::Error messages that indicate the current connection should be disconnected
89
45
  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
46
 
91
47
  set_adapter_scheme :mysql
48
+
49
+ # Hash of conversion procs for the current database
50
+ attr_reader :conversion_procs
51
+ #
52
+ # Whether to convert tinyint columns to bool for the current database
53
+ attr_reader :convert_tinyint_to_bool
54
+
55
+ # By default, Sequel raises an exception if in invalid date or time is used.
56
+ # However, if this is set to nil or :nil, the adapter treats dates
57
+ # like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
58
+ # it returns the strings as is.
59
+ attr_reader :convert_invalid_date_time
92
60
 
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)
61
+ def initialize(opts={})
62
+ super
63
+ @conversion_procs = MYSQL_TYPES.dup
64
+ self.convert_tinyint_to_bool = Sequel::MySQL.convert_tinyint_to_bool
65
+ self.convert_invalid_date_time = Sequel::MySQL.convert_invalid_date_time
97
66
  end
98
-
67
+
99
68
  # Connect to the database. In addition to the usual database options,
100
69
  # the following options have effect:
101
70
  #
@@ -139,7 +108,7 @@ module Sequel
139
108
  opts[:user],
140
109
  opts[:password],
141
110
  opts[:database],
142
- opts[:port],
111
+ (opts[:port].to_i if opts[:port]),
143
112
  opts[:socket],
144
113
  Mysql::CLIENT_MULTI_RESULTS +
145
114
  Mysql::CLIENT_MULTI_STATEMENTS +
@@ -161,30 +130,31 @@ module Sequel
161
130
 
162
131
  sqls.each{|sql| log_yield(sql){conn.query(sql)}}
163
132
 
164
- class << conn
165
- attr_accessor :prepared_statements
166
- end
167
- conn.prepared_statements = {}
133
+ add_prepared_statements_cache(conn)
168
134
  conn
169
135
  end
170
136
 
171
- # Returns instance of Sequel::MySQL::Dataset with the given options.
172
- def dataset(opts = nil)
173
- MySQL::Dataset.new(self, opts)
137
+ # Modify the type translators for the date, time, and timestamp types
138
+ # depending on the value given.
139
+ def convert_invalid_date_time=(v)
140
+ m0 = ::Sequel.method(:string_to_time)
141
+ @conversion_procs[11] = (v != false) ? lambda{|v| convert_date_time(v, &m0)} : m0
142
+ m1 = ::Sequel.method(:string_to_date)
143
+ m = (v != false) ? lambda{|v| convert_date_time(v, &m1)} : m1
144
+ [10, 14].each{|i| @conversion_procs[i] = m}
145
+ m2 = method(:to_application_timestamp)
146
+ m = (v != false) ? lambda{|v| convert_date_time(v, &m2)} : m2
147
+ [7, 12].each{|i| @conversion_procs[i] = m}
148
+ @convert_invalid_date_time = v
174
149
  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
150
+
151
+ # Modify the type translator used for the tinyint type based
152
+ # on the value given.
153
+ def convert_tinyint_to_bool=(v)
154
+ @conversion_procs[1] = TYPE_TRANSLATOR.method(v ? :boolean : :integer)
155
+ @convert_tinyint_to_bool = v
186
156
  end
187
-
157
+
188
158
  # Return the version of the MySQL server two which we are connecting.
189
159
  def server_version(server=nil)
190
160
  @server_version ||= (synchronize(server){|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
@@ -220,7 +190,7 @@ module Sequel
220
190
  end
221
191
  end
222
192
  rescue Mysql::Error => e
223
- raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
193
+ raise_error(e)
224
194
  ensure
225
195
  r.free if r
226
196
  # Use up all results to avoid a commands out of sync message.
@@ -244,10 +214,34 @@ module Sequel
244
214
  :query
245
215
  end
246
216
 
217
+ # If convert_invalid_date_time is nil, :nil, or :string and
218
+ # the conversion raises an InvalidValue exception, return v
219
+ # if :string and nil otherwise.
220
+ def convert_date_time(v)
221
+ begin
222
+ yield v
223
+ rescue InvalidValue
224
+ case @convert_invalid_date_time
225
+ when nil, :nil
226
+ nil
227
+ when :string
228
+ v
229
+ else
230
+ raise
231
+ end
232
+ end
233
+ end
234
+
247
235
  # The MySQL adapter main error class is Mysql::Error
248
236
  def database_error_classes
249
237
  [Mysql::Error]
250
238
  end
239
+
240
+ # Raise a disconnect error if the exception message matches the list
241
+ # of recognized exceptions.
242
+ def disconnect_error?(e, opts)
243
+ super || (e.is_a?(::Mysql::Error) && MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
244
+ end
251
245
 
252
246
  # The database name when using the native adapter is always stored in
253
247
  # the :database option.
@@ -262,97 +256,18 @@ module Sequel
262
256
  nil
263
257
  end
264
258
 
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
259
  # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
287
260
  def schema_column_type(db_type)
288
- Sequel::MySQL.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
261
+ convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
289
262
  end
290
263
  end
291
264
 
292
265
  # Dataset class for MySQL datasets accessed via the native driver.
293
266
  class Dataset < Sequel::Dataset
294
267
  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
268
+ include Sequel::MySQL::PreparedStatements::DatasetMethods
269
+
270
+ Database::DatasetClass = self
356
271
 
357
272
  # Delete rows matching this dataset
358
273
  def delete
@@ -365,11 +280,12 @@ module Sequel
365
280
  def fetch_rows(sql, &block)
366
281
  execute(sql) do |r|
367
282
  i = -1
283
+ cps = db.conversion_procs
368
284
  cols = r.fetch_fields.map do |f|
369
285
  # Pretend tinyint is another integer type if its length is not 1, to
370
286
  # avoid casting to boolean if Sequel::MySQL.convert_tinyint_to_bool
371
287
  # is set.
372
- type_proc = f.type == 1 && f.length != 1 ? MYSQL_TYPES[2] : MYSQL_TYPES[f.type]
288
+ type_proc = f.type == 1 && f.length != 1 ? cps[2] : cps[f.type]
373
289
  [output_identifier(f.name), type_proc, i+=1]
374
290
  end
375
291
  @columns = cols.map{|c| c.first}
@@ -395,18 +311,6 @@ module Sequel
395
311
  execute_dui(insert_sql(*values)){|c| return c.insert_id}
396
312
  end
397
313
 
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
314
  # Replace (update or insert) the matching row.
411
315
  def replace(*args)
412
316
  execute_dui(replace_sql(*args)){|c| return c.insert_id}
@@ -446,13 +350,10 @@ module Sequel
446
350
  end
447
351
 
448
352
  # 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)
353
+ def literal_string_append(sql, v)
354
+ sql << "'"
355
+ sql << ::Mysql.quote(v)
356
+ sql << "'"
456
357
  end
457
358
 
458
359
  # 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|