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,498 @@
1
+ module Sequel
2
+ Dataset::NON_SQL_OPTIONS << :insert_ignore
3
+ Dataset::NON_SQL_OPTIONS << :on_duplicate_key_update
4
+
5
+ module MySQL
6
+ class << self
7
+ # Set the default charset used for CREATE TABLE. You can pass the
8
+ # :charset option to create_table to override this setting.
9
+ attr_accessor :default_charset
10
+
11
+ # Set the default collation used for CREATE TABLE. You can pass the
12
+ # :collate option to create_table to override this setting.
13
+ attr_accessor :default_collate
14
+
15
+ # Set the default engine used for CREATE TABLE. You can pass the
16
+ # :engine option to create_table to override this setting.
17
+ attr_accessor :default_engine
18
+ end
19
+
20
+ # Methods shared by Database instances that connect to MySQL,
21
+ # currently supported by the native and JDBC adapters.
22
+ module DatabaseMethods
23
+ AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
24
+ CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
25
+ PRIMARY = 'PRIMARY'.freeze
26
+
27
+ # MySQL's cast rules are restrictive in that you can't just cast to any possible
28
+ # database type.
29
+ def cast_type_literal(type)
30
+ CAST_TYPES[type] || super
31
+ end
32
+
33
+ # MySQL uses the :mysql database type
34
+ def database_type
35
+ :mysql
36
+ end
37
+
38
+ # Return a hash containing index information. Hash keys are index name symbols.
39
+ # Values are subhashes with two keys, :columns and :unique. The value of :columns
40
+ # is an array of symbols of column names. The value of :unique is true or false
41
+ # depending on if the index is unique.
42
+ #
43
+ # Does not include the primary key index or indexes on partial keys.
44
+ def indexes(table)
45
+ indexes = {}
46
+ remove_indexes = []
47
+ m = output_identifier_meth
48
+ im = input_identifier_meth
49
+ metadata_dataset.with_sql("SHOW INDEX FROM ?", SQL::Identifier.new(im.call(table))).each do |r|
50
+ name = r[:Key_name]
51
+ next if name == PRIMARY
52
+ name = m.call(name)
53
+ remove_indexes << name if r[:Sub_part]
54
+ i = indexes[name] ||= {:columns=>[], :unique=>r[:Non_unique] != 1}
55
+ i[:columns] << m.call(r[:Column_name])
56
+ end
57
+ indexes.reject{|k,v| remove_indexes.include?(k)}
58
+ end
59
+
60
+ # Get version of MySQL server, used for determined capabilities.
61
+ def server_version
62
+ m = /(\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
63
+ @server_version ||= (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
64
+ end
65
+
66
+ # Return an array of symbols specifying table names in the current database.
67
+ #
68
+ # Options:
69
+ # * :server - Set the server to use
70
+ def tables(opts={})
71
+ m = output_identifier_meth
72
+ metadata_dataset.with_sql('SHOW TABLES').server(opts[:server]).map{|r| m.call(r.values.first)}
73
+ end
74
+
75
+ # MySQL supports savepoints
76
+ def supports_savepoints?
77
+ true
78
+ end
79
+
80
+ # Changes the database in use by issuing a USE statement. I would be
81
+ # very careful if I used this.
82
+ def use(db_name)
83
+ disconnect
84
+ @opts[:database] = db_name if self << "USE #{db_name}"
85
+ @schemas = {}
86
+ self
87
+ end
88
+
89
+ private
90
+
91
+ # Use MySQL specific syntax for rename column, set column type, and
92
+ # drop index cases.
93
+ def alter_table_sql(table, op)
94
+ case op[:op]
95
+ when :add_column
96
+ if related = op.delete(:table)
97
+ sql = super(table, op)
98
+ op[:table] = related
99
+ [sql, "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{column_references_sql(op)}"]
100
+ else
101
+ super(table, op)
102
+ end
103
+ when :rename_column, :set_column_type, :set_column_null, :set_column_default
104
+ o = op[:op]
105
+ opts = schema(table).find{|x| x.first == op[:name]}
106
+ opts = opts ? opts.last.dup : {}
107
+ opts[:name] = o == :rename_column ? op[:new_name] : op[:name]
108
+ opts[:type] = o == :set_column_type ? op[:type] : opts[:db_type]
109
+ opts[:null] = o == :set_column_null ? op[:null] : opts[:allow_null]
110
+ opts[:default] = o == :set_column_default ? op[:default] : opts[:ruby_default]
111
+ opts.delete(:default) if opts[:default] == nil
112
+ "ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(opts))}"
113
+ when :drop_index
114
+ "#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
115
+ else
116
+ super(table, op)
117
+ end
118
+ end
119
+
120
+ # Use MySQL specific AUTO_INCREMENT text.
121
+ def auto_increment_sql
122
+ AUTO_INCREMENT
123
+ end
124
+
125
+ # MySQL doesn't allow default values on text columns, so ignore if it the
126
+ # generic text type is used
127
+ def column_definition_sql(column)
128
+ column.delete(:default) if column[:type] == File || (column[:type] == String && column[:text] == true)
129
+ super
130
+ end
131
+
132
+ # Use MySQL specific syntax for engine type and character encoding
133
+ def create_table_sql(name, generator, options = {})
134
+ engine = options.fetch(:engine, Sequel::MySQL.default_engine)
135
+ charset = options.fetch(:charset, Sequel::MySQL.default_charset)
136
+ collate = options.fetch(:collate, Sequel::MySQL.default_collate)
137
+ generator.columns.each do |c|
138
+ if t = c.delete(:table)
139
+ generator.foreign_key([c[:name]], t, c.merge(:name=>nil, :type=>:foreign_key))
140
+ end
141
+ end
142
+ "#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
143
+ end
144
+
145
+ # MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
146
+ def identifier_input_method_default
147
+ nil
148
+ end
149
+
150
+ # MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
151
+ def identifier_output_method_default
152
+ nil
153
+ end
154
+
155
+ # Handle MySQL specific index SQL syntax
156
+ def index_definition_sql(table_name, index)
157
+ index_name = quote_identifier(index[:name] || default_index_name(table_name, index[:columns]))
158
+ index_type = case index[:type]
159
+ when :full_text
160
+ "FULLTEXT "
161
+ when :spatial
162
+ "SPATIAL "
163
+ else
164
+ using = " USING #{index[:type]}" unless index[:type] == nil
165
+ "UNIQUE " if index[:unique]
166
+ end
167
+ "CREATE #{index_type}INDEX #{index_name}#{using} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}"
168
+ end
169
+
170
+ # MySQL treats integer primary keys as autoincrementing.
171
+ def schema_autoincrementing_primary_key?(schema)
172
+ super and schema[:db_type] =~ /int/io
173
+ end
174
+
175
+ # Use the MySQL specific DESCRIBE syntax to get a table description.
176
+ def schema_parse_table(table_name, opts)
177
+ m = output_identifier_meth
178
+ im = input_identifier_meth
179
+ metadata_dataset.with_sql("DESCRIBE ?", SQL::Identifier.new(im.call(table_name))).map do |row|
180
+ row.delete(:Extra)
181
+ row[:allow_null] = row.delete(:Null) == 'YES'
182
+ row[:default] = row.delete(:Default)
183
+ row[:primary_key] = row.delete(:Key) == 'PRI'
184
+ row[:default] = nil if blank_object?(row[:default])
185
+ row[:db_type] = row.delete(:Type)
186
+ row[:type] = schema_column_type(row[:db_type])
187
+ [m.call(row.delete(:Field)), row]
188
+ end
189
+ end
190
+
191
+ # Respect the :size option if given to produce
192
+ # tinyblob, mediumblob, and longblob if :tiny,
193
+ # :medium, or :long is given.
194
+ def type_literal_generic_file(column)
195
+ case column[:size]
196
+ when :tiny # < 2^8 bytes
197
+ :tinyblob
198
+ when :medium # < 2^24 bytes
199
+ :mediumblob
200
+ when :long # < 2^32 bytes
201
+ :longblob
202
+ else # 2^16 bytes
203
+ :blob
204
+ end
205
+ end
206
+
207
+ # MySQL has both datetime and timestamp classes, most people are going
208
+ # to want datetime
209
+ def type_literal_generic_datetime(column)
210
+ :datetime
211
+ end
212
+
213
+ # MySQL has both datetime and timestamp classes, most people are going
214
+ # to want datetime
215
+ def type_literal_generic_time(column)
216
+ column[:only_time] ? :time : :datetime
217
+ end
218
+
219
+ # MySQL doesn't have a true boolean class, so it uses tinyint(1)
220
+ def type_literal_generic_trueclass(column)
221
+ :'tinyint(1)'
222
+ end
223
+ end
224
+
225
+ # Dataset methods shared by datasets that use MySQL databases.
226
+ module DatasetMethods
227
+ BOOL_TRUE = '1'.freeze
228
+ BOOL_FALSE = '0'.freeze
229
+ COMMA_SEPARATOR = ', '.freeze
230
+ FOR_SHARE = ' LOCK IN SHARE MODE'.freeze
231
+ DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from where order limit')
232
+ INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'ignore into columns values on_duplicate_key_update')
233
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit lock')
234
+ UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set where order limit')
235
+
236
+ # MySQL specific syntax for LIKE/REGEXP searches, as well as
237
+ # string concatenation.
238
+ def complex_expression_sql(op, args)
239
+ case op
240
+ when :~, :'!~', :'~*', :'!~*', :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
241
+ "(#{literal(args.at(0))} #{'NOT ' if [:'NOT LIKE', :'NOT ILIKE', :'!~', :'!~*'].include?(op)}#{[:~, :'!~', :'~*', :'!~*'].include?(op) ? 'REGEXP' : 'LIKE'} #{'BINARY ' if [:~, :'!~', :LIKE, :'NOT LIKE'].include?(op)}#{literal(args.at(1))})"
242
+ when :'||'
243
+ if args.length > 1
244
+ "CONCAT(#{args.collect{|a| literal(a)}.join(', ')})"
245
+ else
246
+ literal(args.at(0))
247
+ end
248
+ else
249
+ super(op, args)
250
+ end
251
+ end
252
+
253
+ # Use GROUP BY instead of DISTINCT ON if arguments are provided.
254
+ def distinct(*args)
255
+ args.empty? ? super : group(*args)
256
+ end
257
+
258
+ # Return a cloned dataset which will use LOCK IN SHARE MODE to lock returned rows.
259
+ def for_share
260
+ lock_style(:share)
261
+ end
262
+
263
+ # Adds full text filter
264
+ def full_text_search(cols, terms, opts = {})
265
+ filter(full_text_sql(cols, terms, opts))
266
+ end
267
+
268
+ # MySQL specific full text search syntax.
269
+ def full_text_sql(cols, term, opts = {})
270
+ "MATCH #{literal(Array(cols))} AGAINST (#{literal(Array(term).join(' '))}#{" IN BOOLEAN MODE" if opts[:boolean]})"
271
+ end
272
+
273
+ # MySQL allows HAVING clause on ungrouped datasets.
274
+ def having(*cond, &block)
275
+ _filter(:having, *cond, &block)
276
+ end
277
+
278
+ # Transforms an CROSS JOIN to an INNER JOIN if the expr is not nil.
279
+ # Raises an error on use of :full_outer type, since MySQL doesn't support it.
280
+ def join_table(type, table, expr=nil, table_alias={})
281
+ type = :inner if (type == :cross) && !expr.nil?
282
+ raise(Sequel::Error, "MySQL doesn't support FULL OUTER JOIN") if type == :full_outer
283
+ super(type, table, expr, table_alias)
284
+ end
285
+
286
+ # Transforms :natural_inner to NATURAL LEFT JOIN and straight to
287
+ # STRAIGHT_JOIN.
288
+ def join_type_sql(join_type)
289
+ case join_type
290
+ when :straight then 'STRAIGHT_JOIN'
291
+ when :natural_inner then 'NATURAL LEFT JOIN'
292
+ else super
293
+ end
294
+ end
295
+
296
+ # Sets up multi_insert or import to use INSERT IGNORE.
297
+ # Useful if you have a unique key and want to just skip
298
+ # inserting rows that violate the unique key restriction.
299
+ #
300
+ # Example:
301
+ #
302
+ # dataset.insert_ignore.multi_insert(
303
+ # [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
304
+ # )
305
+ #
306
+ # INSERT IGNORE INTO tablename (name, value) VALUES (a, 1), (b, 2)
307
+ #
308
+ def insert_ignore
309
+ clone(:insert_ignore=>true)
310
+ end
311
+
312
+ # Sets up multi_insert or import to use ON DUPLICATE KEY UPDATE
313
+ # If you pass no arguments, ALL fields will be
314
+ # updated with the new values. If you pass the fields you
315
+ # want then ONLY those field will be updated.
316
+ #
317
+ # Useful if you have a unique key and want to update
318
+ # inserting rows that violate the unique key restriction.
319
+ #
320
+ # Examples:
321
+ #
322
+ # dataset.on_duplicate_key_update.multi_insert(
323
+ # [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
324
+ # )
325
+ #
326
+ # INSERT INTO tablename (name, value) VALUES (a, 1), (b, 2)
327
+ # ON DUPLICATE KEY UPDATE name=VALUES(name), value=VALUES(value)
328
+ #
329
+ # dataset.on_duplicate_key_update(:value).multi_insert(
330
+ # [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
331
+ # )
332
+ #
333
+ # INSERT INTO tablename (name, value) VALUES (a, 1), (b, 2)
334
+ # ON DUPLICATE KEY UPDATE value=VALUES(value)
335
+ #
336
+ def on_duplicate_key_update(*args)
337
+ clone(:on_duplicate_key_update => args)
338
+ end
339
+
340
+ # MySQL specific syntax for inserting multiple values at once.
341
+ def multi_insert_sql(columns, values)
342
+ [insert_sql(columns, LiteralString.new('VALUES ' + values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)))]
343
+ end
344
+
345
+ # MySQL uses the number of rows actually modified in the update,
346
+ # instead of the number of matched by the filter.
347
+ def provides_accurate_rows_matched?
348
+ false
349
+ end
350
+
351
+ # MySQL uses the nonstandard ` (backtick) for quoting identifiers.
352
+ def quoted_identifier(c)
353
+ "`#{c}`"
354
+ end
355
+
356
+ # MySQL specific syntax for REPLACE (aka UPSERT, or update if exists,
357
+ # insert if it doesn't).
358
+ def replace_sql(*values)
359
+ clone(:replace=>true).insert_sql(*values)
360
+ end
361
+
362
+ # MySQL can emulate DISTINCT ON with its non-standard GROUP BY implementation,
363
+ # though the rows returned cannot be made deterministic through ordering.
364
+ def supports_distinct_on?
365
+ true
366
+ end
367
+
368
+ # MySQL does not support INTERSECT or EXCEPT
369
+ def supports_intersect_except?
370
+ false
371
+ end
372
+
373
+ # MySQL supports modifying joined datasets
374
+ def supports_modifying_joins?
375
+ true
376
+ end
377
+
378
+ # MySQL does support fractional timestamps in literal timestamps, but it
379
+ # ignores them. Also, using them seems to cause problems on 1.9. Since
380
+ # they are ignored anyway, not using them is probably best.
381
+ def supports_timestamp_usecs?
382
+ false
383
+ end
384
+
385
+ protected
386
+
387
+ # If this is an replace instead of an insert, use replace instead
388
+ def _insert_sql
389
+ @opts[:replace] ? clause_sql(:replace) : super
390
+ end
391
+
392
+ private
393
+
394
+ # MySQL supports the ORDER BY and LIMIT clauses for DELETE statements
395
+ def delete_clause_methods
396
+ DELETE_CLAUSE_METHODS
397
+ end
398
+
399
+ # Consider the first table in the joined dataset is the table to delete
400
+ # from, but include the others for the purposes of selecting rows.
401
+ def delete_from_sql(sql)
402
+ if joined_dataset?
403
+ sql << " #{source_list(@opts[:from][0..0])} FROM #{source_list(@opts[:from])}"
404
+ select_join_sql(sql)
405
+ else
406
+ super
407
+ end
408
+ end
409
+
410
+ # MySQL supports the IGNORE and ON DUPLICATE KEY UPDATE clauses for INSERT statements
411
+ def insert_clause_methods
412
+ INSERT_CLAUSE_METHODS
413
+ end
414
+ alias replace_clause_methods insert_clause_methods
415
+
416
+ # MySQL doesn't use the SQL standard DEFAULT VALUES.
417
+ def insert_columns_sql(sql)
418
+ values = opts[:values]
419
+ if values.is_a?(Array) && values.empty?
420
+ sql << " ()"
421
+ else
422
+ super
423
+ end
424
+ end
425
+
426
+ # MySQL supports INSERT IGNORE INTO
427
+ def insert_ignore_sql(sql)
428
+ sql << " IGNORE" if opts[:insert_ignore]
429
+ end
430
+
431
+ # MySQL supports INSERT ... ON DUPLICATE KEY UPDATE
432
+ def insert_on_duplicate_key_update_sql(sql)
433
+ sql << on_duplicate_key_update_sql if opts[:on_duplicate_key_update]
434
+ end
435
+
436
+ # MySQL doesn't use the standard DEFAULT VALUES for empty values.
437
+ def insert_values_sql(sql)
438
+ values = opts[:values]
439
+ if values.is_a?(Array) && values.empty?
440
+ sql << " VALUES ()"
441
+ else
442
+ super
443
+ end
444
+ end
445
+
446
+ # MySQL allows a LIMIT in DELETE and UPDATE statements.
447
+ def limit_sql(sql)
448
+ sql << " LIMIT #{@opts[:limit]}" if @opts[:limit]
449
+ end
450
+ alias delete_limit_sql limit_sql
451
+ alias update_limit_sql limit_sql
452
+
453
+ # Use 0 for false on MySQL
454
+ def literal_false
455
+ BOOL_FALSE
456
+ end
457
+
458
+ # Use 1 for true on MySQL
459
+ def literal_true
460
+ BOOL_TRUE
461
+ end
462
+
463
+ # MySQL specific syntax for ON DUPLICATE KEY UPDATE
464
+ def on_duplicate_key_update_sql
465
+ if update_cols = opts[:on_duplicate_key_update]
466
+ update_vals = nil
467
+
468
+ if update_cols.empty?
469
+ update_cols = columns
470
+ elsif update_cols.last.is_a?(Hash)
471
+ update_vals = update_cols.last
472
+ update_cols = update_cols[0..-2]
473
+ end
474
+
475
+ updating = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})" }
476
+ updating += update_vals.map{|c,v| "#{quote_identifier(c)}=#{literal(v)}" } if update_vals
477
+
478
+ " ON DUPLICATE KEY UPDATE #{updating.join(COMMA_SEPARATOR)}"
479
+ end
480
+ end
481
+
482
+ # MySQL does not support the SQL WITH clause for SELECT statements
483
+ def select_clause_methods
484
+ SELECT_CLAUSE_METHODS
485
+ end
486
+
487
+ # Support FOR SHARE locking when using the :share lock style.
488
+ def select_lock_sql(sql)
489
+ @opts[:lock] == :share ? (sql << FOR_SHARE) : super
490
+ end
491
+
492
+ # MySQL supports the ORDER BY and LIMIT clauses for UPDATE statements
493
+ def update_clause_methods
494
+ UPDATE_CLAUSE_METHODS
495
+ end
496
+ end
497
+ end
498
+ end