viking-sequel 3.10.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 (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,55 @@
1
+ Sequel.require 'adapters/shared/sqlite'
2
+
3
+ module Sequel
4
+ module JDBC
5
+ # Database and Dataset support for SQLite databases accessed via JDBC.
6
+ module SQLite
7
+ # Instance methods for SQLite Database objects accessed via JDBC.
8
+ module DatabaseMethods
9
+ include Sequel::SQLite::DatabaseMethods
10
+
11
+ # Return Sequel::JDBC::SQLite::Dataset object with the given opts.
12
+ def dataset(opts=nil)
13
+ Sequel::JDBC::SQLite::Dataset.new(self, opts)
14
+ end
15
+
16
+ private
17
+
18
+ # Use last_insert_rowid() to get the last inserted id.
19
+ def last_insert_id(conn, opts={})
20
+ stmt = conn.createStatement
21
+ begin
22
+ rs = stmt.executeQuery('SELECT last_insert_rowid()')
23
+ rs.next
24
+ rs.getInt(1)
25
+ ensure
26
+ stmt.close
27
+ end
28
+ end
29
+
30
+ # Default to a single connection for a memory database.
31
+ def connection_pool_default_options
32
+ o = super
33
+ uri == 'jdbc:sqlite::memory:' ? o.merge(:max_connections=>1) : o
34
+ end
35
+
36
+ # Execute the connection pragmas on the connection.
37
+ def setup_connection(conn)
38
+ conn = super(conn)
39
+ begin
40
+ stmt = conn.createStatement
41
+ connection_pragmas.each{|s| log_yield(s){stmt.execute(s)}}
42
+ ensure
43
+ stmt.close if stmt
44
+ end
45
+ conn
46
+ end
47
+ end
48
+
49
+ # Dataset class for SQLite datasets accessed via JDBC.
50
+ class Dataset < JDBC::Dataset
51
+ include Sequel::SQLite::DatasetMethods
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,421 @@
1
+ begin
2
+ require "mysqlplus"
3
+ rescue LoadError
4
+ require 'mysql'
5
+ end
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
+
8
+ Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
9
+
10
+ module Sequel
11
+ # Module for holding all MySQL-related classes and modules for Sequel.
12
+ module MySQL
13
+ # Mapping of type numbers to conversion procs
14
+ MYSQL_TYPES = {}
15
+
16
+ # Use only a single proc for each type to save on memory
17
+ MYSQL_TYPE_PROCS = {
18
+ [0, 246] => lambda{|v| BigDecimal.new(v)}, # decimal
19
+ [1] => lambda{|v| convert_tinyint_to_bool ? v.to_i != 0 : v.to_i}, # tinyint
20
+ [2, 3, 8, 9, 13, 247, 248] => lambda{|v| v.to_i}, # integer
21
+ [4, 5] => lambda{|v| v.to_f}, # float
22
+ [10, 14] => lambda{|v| convert_date_time(:string_to_date, v)}, # date
23
+ [7, 12] => lambda{|v| convert_date_time(:database_to_application_timestamp, v)}, # datetime
24
+ [11] => lambda{|v| convert_date_time(:string_to_time, v)}, # time
25
+ [249, 250, 251, 252] => lambda{|v| Sequel::SQL::Blob.new(v)} # blob
26
+ }
27
+ MYSQL_TYPE_PROCS.each do |k,v|
28
+ k.each{|n| MYSQL_TYPES[n] = v}
29
+ end
30
+
31
+ @convert_invalid_date_time = false
32
+ @convert_tinyint_to_bool = true
33
+
34
+ class << self
35
+ # By default, Sequel raises an exception if in invalid date or time is used.
36
+ # However, if this is set to nil or :nil, the adapter treats dates
37
+ # like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
38
+ # it returns the strings as is.
39
+ attr_accessor :convert_invalid_date_time
40
+
41
+ # Sequel converts the column type tinyint(1) to a boolean by default when
42
+ # using the native MySQL adapter. You can turn off the conversion by setting
43
+ # this to false.
44
+ attr_accessor :convert_tinyint_to_bool
45
+ end
46
+
47
+ # If convert_invalid_date_time is nil, :nil, or :string and
48
+ # the conversion raises an InvalidValue exception, return v
49
+ # if :string and nil otherwise.
50
+ def self.convert_date_time(meth, v)
51
+ begin
52
+ Sequel.send(meth, v)
53
+ rescue InvalidValue
54
+ case @convert_invalid_date_time
55
+ when nil, :nil
56
+ nil
57
+ when :string
58
+ v
59
+ else
60
+ raise
61
+ end
62
+ end
63
+ end
64
+
65
+ # Database class for MySQL databases used with Sequel.
66
+ class Database < Sequel::Database
67
+ include Sequel::MySQL::DatabaseMethods
68
+
69
+ # Mysql::Error messages that indicate the current connection should be disconnected
70
+ 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)/
71
+
72
+ set_adapter_scheme :mysql
73
+
74
+ # Support stored procedures on MySQL
75
+ def call_sproc(name, opts={}, &block)
76
+ args = opts[:args] || []
77
+ execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
78
+ end
79
+
80
+ # Connect to the database. In addition to the usual database options,
81
+ # the following options have effect:
82
+ #
83
+ # * :auto_is_null - Set to true to use MySQL default behavior of having
84
+ # a filter for an autoincrement column equals NULL to return the last
85
+ # inserted row.
86
+ # * :charset - Same as :encoding (:encoding takes precendence)
87
+ # * :compress - Set to false to not compress results from the server
88
+ # * :config_default_group - The default group to read from the in
89
+ # the MySQL config file.
90
+ # * :config_local_infile - If provided, sets the Mysql::OPT_LOCAL_INFILE
91
+ # option on the connection with the given value.
92
+ # * :encoding - Set all the related character sets for this
93
+ # connection (connection, client, database, server, and results).
94
+ # * :socket - Use a unix socket file instead of connecting via TCP/IP.
95
+ # * :timeout - Set the timeout in seconds before the server will
96
+ # disconnect this connection.
97
+ def connect(server)
98
+ opts = server_opts(server)
99
+ conn = Mysql.init
100
+ conn.options(Mysql::READ_DEFAULT_GROUP, opts[:config_default_group] || "client")
101
+ conn.options(Mysql::OPT_LOCAL_INFILE, opts[:config_local_infile]) if opts.has_key?(:config_local_infile)
102
+ if encoding = opts[:encoding] || opts[:charset]
103
+ # set charset _before_ the connect. using an option instead of "SET (NAMES|CHARACTER_SET_*)" works across reconnects
104
+ conn.options(Mysql::SET_CHARSET_NAME, encoding)
105
+ end
106
+ conn.real_connect(
107
+ opts[:host] || 'localhost',
108
+ opts[:user],
109
+ opts[:password],
110
+ opts[:database],
111
+ opts[:port],
112
+ opts[:socket],
113
+ Mysql::CLIENT_MULTI_RESULTS +
114
+ Mysql::CLIENT_MULTI_STATEMENTS +
115
+ (opts[:compress] == false ? 0 : Mysql::CLIENT_COMPRESS)
116
+ )
117
+
118
+ # increase timeout so mysql server doesn't disconnect us
119
+ conn.query("set @@wait_timeout = #{opts[:timeout] || 2592000}")
120
+
121
+ # By default, MySQL 'where id is null' selects the last inserted id
122
+ conn.query("set SQL_AUTO_IS_NULL=0") unless opts[:auto_is_null]
123
+
124
+ class << conn
125
+ attr_accessor :prepared_statements
126
+ end
127
+ conn.prepared_statements = {}
128
+ conn
129
+ end
130
+
131
+ # Returns instance of Sequel::MySQL::Dataset with the given options.
132
+ def dataset(opts = nil)
133
+ MySQL::Dataset.new(self, opts)
134
+ end
135
+
136
+ # Executes the given SQL using an available connection, yielding the
137
+ # connection if the block is given.
138
+ def execute(sql, opts={}, &block)
139
+ if opts[:sproc]
140
+ call_sproc(sql, opts, &block)
141
+ elsif sql.is_a?(Symbol)
142
+ execute_prepared_statement(sql, opts, &block)
143
+ else
144
+ synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
145
+ end
146
+ end
147
+
148
+ # Return the version of the MySQL server two which we are connecting.
149
+ def server_version(server=nil)
150
+ @server_version ||= (synchronize(server){|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
151
+ end
152
+
153
+ private
154
+
155
+ # Execute the given SQL on the given connection. If the :type
156
+ # option is :select, yield the result of the query, otherwise
157
+ # yield the connection if a block is given.
158
+ def _execute(conn, sql, opts)
159
+ begin
160
+ r = log_yield(sql){conn.query(sql)}
161
+ if opts[:type] == :select
162
+ yield r if r
163
+ elsif block_given?
164
+ yield conn
165
+ end
166
+ if conn.respond_to?(:more_results?)
167
+ while conn.more_results? do
168
+ if r
169
+ r.free
170
+ r = nil
171
+ end
172
+ begin
173
+ conn.next_result
174
+ r = conn.use_result
175
+ rescue Mysql::Error => e
176
+ raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
177
+ break
178
+ end
179
+ yield r if opts[:type] == :select
180
+ end
181
+ end
182
+ rescue Mysql::Error => e
183
+ raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
184
+ ensure
185
+ r.free if r
186
+ # Use up all results to avoid a commands out of sync message.
187
+ if conn.respond_to?(:more_results?)
188
+ while conn.more_results? do
189
+ begin
190
+ conn.next_result
191
+ r = conn.use_result
192
+ rescue Mysql::Error => e
193
+ raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
194
+ break
195
+ end
196
+ r.free if r
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ # MySQL connections use the query method to execute SQL without a result
203
+ def connection_execute_method
204
+ :query
205
+ end
206
+
207
+ # The MySQL adapter main error class is Mysql::Error
208
+ def database_error_classes
209
+ [Mysql::Error]
210
+ end
211
+
212
+ # The database name when using the native adapter is always stored in
213
+ # the :database option.
214
+ def database_name
215
+ @opts[:database]
216
+ end
217
+
218
+ # Closes given database connection.
219
+ def disconnect_connection(c)
220
+ c.close
221
+ end
222
+
223
+ # Executes a prepared statement on an available connection. If the
224
+ # prepared statement already exists for the connection and has the same
225
+ # SQL, reuse it, otherwise, prepare the new statement. Because of the
226
+ # usual MySQL stupidity, we are forced to name arguments via separate
227
+ # SET queries. Use @sequel_arg_N (for N starting at 1) for these
228
+ # arguments.
229
+ def execute_prepared_statement(ps_name, opts, &block)
230
+ args = opts[:arguments]
231
+ ps = prepared_statements[ps_name]
232
+ sql = ps.prepared_sql
233
+ synchronize(opts[:server]) do |conn|
234
+ unless conn.prepared_statements[ps_name] == sql
235
+ conn.prepared_statements[ps_name] = sql
236
+ _execute(conn, "PREPARE #{ps_name} FROM '#{::Mysql.quote(sql)}'", opts)
237
+ end
238
+ i = 0
239
+ _execute(conn, "SET " + args.map {|arg| "@sequel_arg_#{i+=1} = #{literal(arg)}"}.join(", "), opts) unless args.empty?
240
+ _execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
241
+ end
242
+ end
243
+
244
+ # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
245
+ def schema_column_type(db_type)
246
+ Sequel::MySQL.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
247
+ end
248
+ end
249
+
250
+ # Dataset class for MySQL datasets accessed via the native driver.
251
+ class Dataset < Sequel::Dataset
252
+ include Sequel::MySQL::DatasetMethods
253
+ include StoredProcedures
254
+
255
+ # Methods to add to MySQL prepared statement calls without using a
256
+ # real database prepared statement and bound variables.
257
+ module CallableStatementMethods
258
+ # Extend given dataset with this module so subselects inside subselects in
259
+ # prepared statements work.
260
+ def subselect_sql(ds)
261
+ ps = ds.to_prepared_statement(:select)
262
+ ps.extend(CallableStatementMethods)
263
+ ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
264
+ ps.prepared_args = prepared_args
265
+ ps.prepared_sql
266
+ end
267
+ end
268
+
269
+ # Methods for MySQL prepared statements using the native driver.
270
+ module PreparedStatementMethods
271
+ include Sequel::Dataset::UnnumberedArgumentMapper
272
+
273
+ private
274
+
275
+ # Execute the prepared statement with the bind arguments instead of
276
+ # the given SQL.
277
+ def execute(sql, opts={}, &block)
278
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
279
+ end
280
+
281
+ # Same as execute, explicit due to intricacies of alias and super.
282
+ def execute_dui(sql, opts={}, &block)
283
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
284
+ end
285
+ end
286
+
287
+ # Methods for MySQL stored procedures using the native driver.
288
+ module StoredProcedureMethods
289
+ include Sequel::Dataset::StoredProcedureMethods
290
+
291
+ private
292
+
293
+ # Execute the database stored procedure with the stored arguments.
294
+ def execute(sql, opts={}, &block)
295
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
296
+ end
297
+
298
+ # Same as execute, explicit due to intricacies of alias and super.
299
+ def execute_dui(sql, opts={}, &block)
300
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
301
+ end
302
+ end
303
+
304
+ # MySQL is different in that it supports prepared statements but not bound
305
+ # variables outside of prepared statements. The default implementation
306
+ # breaks the use of subselects in prepared statements, so extend the
307
+ # temporary prepared statement that this creates with a module that
308
+ # fixes it.
309
+ def call(type, bind_arguments={}, *values, &block)
310
+ ps = to_prepared_statement(type, values)
311
+ ps.extend(CallableStatementMethods)
312
+ ps.call(bind_arguments, &block)
313
+ end
314
+
315
+ # Delete rows matching this dataset
316
+ def delete
317
+ execute_dui(delete_sql){|c| return c.affected_rows}
318
+ end
319
+
320
+ # Yield all rows matching this dataset. If the dataset is set to
321
+ # split multiple statements, yield arrays of hashes one per statement
322
+ # instead of yielding results for all statements as hashes.
323
+ def fetch_rows(sql, &block)
324
+ execute(sql) do |r|
325
+ i = -1
326
+ cols = r.fetch_fields.map{|f| [output_identifier(f.name), MYSQL_TYPES[f.type], i+=1]}
327
+ @columns = cols.map{|c| c.first}
328
+ if opts[:split_multiple_result_sets]
329
+ s = []
330
+ yield_rows(r, cols){|h| s << h}
331
+ yield s
332
+ else
333
+ yield_rows(r, cols, &block)
334
+ end
335
+ end
336
+ self
337
+ end
338
+
339
+ # Don't allow graphing a dataset that splits multiple statements
340
+ def graph(*)
341
+ raise(Error, "Can't graph a dataset that splits multiple result sets") if opts[:split_multiple_result_sets]
342
+ super
343
+ end
344
+
345
+ # Insert a new value into this dataset
346
+ def insert(*values)
347
+ execute_dui(insert_sql(*values)){|c| return c.insert_id}
348
+ end
349
+
350
+ # Store the given type of prepared statement in the associated database
351
+ # with the given name.
352
+ def prepare(type, name=nil, *values)
353
+ ps = to_prepared_statement(type, values)
354
+ ps.extend(PreparedStatementMethods)
355
+ if name
356
+ ps.prepared_statement_name = name
357
+ db.prepared_statements[name] = ps
358
+ end
359
+ ps
360
+ end
361
+
362
+ # Replace (update or insert) the matching row.
363
+ def replace(*args)
364
+ execute_dui(replace_sql(*args)){|c| return c.insert_id}
365
+ end
366
+
367
+ # Makes each yield arrays of rows, with each array containing the rows
368
+ # for a given result set. Does not work with graphing. So you can submit
369
+ # SQL with multiple statements and easily determine which statement
370
+ # returned which results.
371
+ #
372
+ # Modifies the row_proc of the returned dataset so that it still works
373
+ # as expected (running on the hashes instead of on the arrays of hashes).
374
+ # If you modify the row_proc afterward, note that it will receive an array
375
+ # of hashes instead of a hash.
376
+ def split_multiple_result_sets
377
+ raise(Error, "Can't split multiple statements on a graphed dataset") if opts[:graph]
378
+ ds = clone(:split_multiple_result_sets=>true)
379
+ ds.row_proc = proc{|x| x.map{|h| row_proc.call(h)}} if row_proc
380
+ ds
381
+ end
382
+
383
+ # Update the matching rows.
384
+ def update(values={})
385
+ execute_dui(update_sql(values)){|c| return c.affected_rows}
386
+ end
387
+
388
+ private
389
+
390
+ # Set the :type option to :select if it hasn't been set.
391
+ def execute(sql, opts={}, &block)
392
+ super(sql, {:type=>:select}.merge(opts), &block)
393
+ end
394
+
395
+ # Set the :type option to :dui if it hasn't been set.
396
+ def execute_dui(sql, opts={}, &block)
397
+ super(sql, {:type=>:dui}.merge(opts), &block)
398
+ end
399
+
400
+ # Handle correct quoting of strings using ::MySQL.quote.
401
+ def literal_string(v)
402
+ "'#{::Mysql.quote(v)}'"
403
+ end
404
+
405
+ # Extend the dataset with the MySQL stored procedure methods.
406
+ def prepare_extend_sproc(ds)
407
+ ds.extend(StoredProcedureMethods)
408
+ end
409
+
410
+ # Yield each row of the given result set r with columns cols
411
+ # as a hash with symbol keys
412
+ def yield_rows(r, cols)
413
+ while row = r.fetch_row
414
+ h = {}
415
+ cols.each{|n, p, i| v = row[i]; h[n] = (v && p) ? p.call(v) : v}
416
+ yield h
417
+ end
418
+ end
419
+ end
420
+ end
421
+ end