activerecord-jdbc-adapter 5.0.pre1 → 51.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 (68) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -2
  3. data/.travis.yml +15 -416
  4. data/Gemfile +35 -37
  5. data/README.md +23 -118
  6. data/RUNNING_TESTS.md +31 -26
  7. data/Rakefile +2 -3
  8. data/activerecord-jdbc-adapter.gemspec +1 -2
  9. data/lib/arjdbc/abstract/connection_management.rb +21 -0
  10. data/lib/arjdbc/abstract/core.rb +62 -0
  11. data/lib/arjdbc/abstract/database_statements.rb +46 -0
  12. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  13. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  14. data/lib/arjdbc/derby/adapter.rb +6 -1
  15. data/lib/arjdbc/discover.rb +0 -7
  16. data/lib/arjdbc/firebird/adapter.rb +2 -2
  17. data/lib/arjdbc/jdbc/adapter.rb +10 -252
  18. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  19. data/lib/arjdbc/jdbc/connection.rb +6 -0
  20. data/lib/arjdbc/jdbc.rb +2 -2
  21. data/lib/arjdbc/mysql/adapter.rb +87 -944
  22. data/lib/arjdbc/mysql/connection_methods.rb +4 -2
  23. data/lib/arjdbc/postgresql/adapter.rb +288 -1023
  24. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  25. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  26. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -5
  27. data/lib/arjdbc/postgresql/column.rb +10 -599
  28. data/lib/arjdbc/postgresql/connection_methods.rb +9 -0
  29. data/lib/arjdbc/postgresql/name.rb +24 -0
  30. data/lib/arjdbc/postgresql/oid_types.rb +25 -110
  31. data/lib/arjdbc/sqlite3/adapter.rb +171 -170
  32. data/lib/arjdbc/tasks/database_tasks.rb +1 -3
  33. data/lib/arjdbc/tasks/db2_database_tasks.rb +2 -2
  34. data/lib/arjdbc/version.rb +1 -1
  35. data/pom.xml +3 -3
  36. data/rakelib/02-test.rake +0 -12
  37. data/rakelib/compile.rake +1 -1
  38. data/rakelib/db.rake +7 -5
  39. data/rakelib/rails.rake +63 -64
  40. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +1 -17
  41. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +518 -1260
  42. data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
  43. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +53 -134
  44. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -240
  45. data/src/java/arjdbc/sqlite3/SQLite3Module.java +0 -20
  46. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +85 -10
  47. metadata +20 -34
  48. data/Appraisals +0 -41
  49. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
  50. data/lib/arjdbc/common_jdbc_methods.rb +0 -89
  51. data/lib/arjdbc/mysql/bulk_change_table.rb +0 -150
  52. data/lib/arjdbc/mysql/column.rb +0 -162
  53. data/lib/arjdbc/mysql/explain_support.rb +0 -82
  54. data/lib/arjdbc/mysql/schema_creation.rb +0 -58
  55. data/lib/arjdbc/oracle/adapter.rb +0 -952
  56. data/lib/arjdbc/oracle/column.rb +0 -126
  57. data/lib/arjdbc/oracle/connection_methods.rb +0 -21
  58. data/lib/arjdbc/oracle.rb +0 -4
  59. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +0 -21
  60. data/lib/arjdbc/postgresql/base/oid.rb +0 -412
  61. data/lib/arjdbc/postgresql/base/schema_definitions.rb +0 -131
  62. data/lib/arjdbc/postgresql/explain_support.rb +0 -53
  63. data/lib/arjdbc/postgresql/oid/bytea.rb +0 -2
  64. data/lib/arjdbc/postgresql/schema_creation.rb +0 -60
  65. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +0 -297
  66. data/lib/arjdbc/tasks/oracle_database_tasks.rb +0 -65
  67. data/src/java/arjdbc/oracle/OracleModule.java +0 -75
  68. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +0 -465
@@ -1,1001 +1,144 @@
1
1
  ArJdbc.load_java_part :MySQL
2
2
 
3
3
  require 'bigdecimal'
4
+ require 'active_record/connection_adapters/abstract_mysql_adapter'
4
5
  require 'active_record/connection_adapters/abstract/schema_definitions'
6
+ require 'arjdbc/abstract/core'
7
+ require 'arjdbc/abstract/connection_management'
8
+ require 'arjdbc/abstract/database_statements'
9
+ require 'arjdbc/abstract/statement_cache'
10
+ require 'arjdbc/abstract/transaction_support'
5
11
 
6
- module ArJdbc
7
- module MySQL
8
-
9
- require 'arjdbc/mysql/column'
10
- require 'arjdbc/mysql/bulk_change_table'
11
- require 'arjdbc/mysql/explain_support'
12
- require 'arjdbc/mysql/schema_creation' # AR 4.x
13
-
14
- include BulkChangeTable if const_defined? :BulkChangeTable
15
-
16
- # @private
17
- ActiveRecordError = ::ActiveRecord::ActiveRecordError
18
-
19
- # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
20
- def self.jdbc_connection_class
21
- ::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
22
- end
23
-
24
- def jdbc_column_class
25
- ::ActiveRecord::ConnectionAdapters::MysqlAdapter::Column
26
- end
27
-
28
- # @private
29
- def init_connection(jdbc_connection)
30
- meta = jdbc_connection.meta_data
31
- if meta.driver_major_version == 1 # TODO check in driver code
32
- # assumes MariaDB 1.x currently
33
- elsif meta.driver_major_version < 5
34
- raise ::ActiveRecord::ConnectionNotEstablished,
35
- "MySQL adapter requires driver >= 5.0 got: '#{meta.driver_version}'"
36
- elsif meta.driver_major_version == 5 && meta.driver_minor_version < 1
37
- config[:connection_alive_sql] ||= 'SELECT 1' # need 5.1 for JDBC 4.0
38
- else
39
- # NOTE: since the loaded Java driver class can't change :
40
- MySQL.send(:remove_method, :init_connection) rescue nil
41
- end
42
- end
43
-
44
- def configure_connection
45
- variables = config[:variables] || {}
46
- # By default, MySQL 'where id is null' selects the last inserted id. Turn this off.
47
- variables[:sql_auto_is_null] = 0 # execute "SET SQL_AUTO_IS_NULL=0"
48
-
49
- # Increase timeout so the server doesn't disconnect us.
50
- wait_timeout = config[:wait_timeout]
51
- wait_timeout = self.class.type_cast_config_to_integer(wait_timeout)
52
- variables[:wait_timeout] = wait_timeout.is_a?(Fixnum) ? wait_timeout : 2147483
53
-
54
- # Make MySQL reject illegal values rather than truncating or blanking them, see
55
- # http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
56
- # If the user has provided another value for sql_mode, don't replace it.
57
- if strict_mode? && ! variables.has_key?(:sql_mode)
58
- variables[:sql_mode] = 'STRICT_ALL_TABLES' # SET SQL_MODE='STRICT_ALL_TABLES'
59
- end
60
-
61
- # NAMES does not have an equals sign, see
62
- # http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
63
- # (trailing comma because variable_assignments will always have content)
64
- encoding = "NAMES #{config[:encoding]}, " if config[:encoding]
65
-
66
- # Gather up all of the SET variables...
67
- variable_assignments = variables.map do |k, v|
68
- if v == ':default' || v == :default
69
- "@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default
70
- elsif ! v.nil?
71
- "@@SESSION.#{k.to_s} = #{quote(v)}"
72
- end
73
- # or else nil; compact to clear nils out
74
- end.compact.join(', ')
75
-
76
- # ...and send them all in one query
77
- execute("SET #{encoding} #{variable_assignments}", :skip_logging)
78
- end
79
-
80
- def strict_mode?
81
- config.key?(:strict) ?
82
- self.class.type_cast_config_to_boolean(config[:strict]) :
83
- AR40 # strict_mode is default since AR 4.0
84
- end
85
-
86
- # @private
87
- @@emulate_booleans = true
88
-
89
- # Boolean emulation can be disabled using (or using the adapter method) :
90
- #
91
- # ArJdbc::MySQL.emulate_booleans = false
92
- #
93
- # @see ActiveRecord::ConnectionAdapters::MysqlAdapter#emulate_booleans
94
- def self.emulate_booleans?; @@emulate_booleans; end
95
- # @deprecated Use {#emulate_booleans?} instead.
96
- def self.emulate_booleans; @@emulate_booleans; end
97
- # @see #emulate_booleans?
98
- def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end
99
-
100
- NATIVE_DATABASE_TYPES = {
101
- :primary_key => "int(11) auto_increment PRIMARY KEY",
102
- :string => { :name => "varchar", :limit => 255 },
103
- :text => { :name => "text" },
104
- :integer => { :name => "int", :limit => 4 },
105
- :float => { :name => "float" },
106
- # :double => { :name=>"double", :limit=>17 }
107
- # :real => { :name=>"real", :limit=>17 }
108
- :numeric => { :name => "numeric" }, # :limit => 65
109
- :decimal => { :name => "decimal" }, # :limit => 65
110
- :datetime => { :name => "datetime" },
111
- # TIMESTAMP has varying properties depending on MySQL version (SQL mode)
112
- :timestamp => { :name => "datetime" },
113
- :time => { :name => "time" },
114
- :date => { :name => "date" },
115
- :binary => { :name => "blob" },
116
- :boolean => { :name => "tinyint", :limit => 1 },
117
- # AR-JDBC added :
118
- :bit => { :name => "bit" }, # :limit => 1
119
- :enum => { :name => "enum" },
120
- :set => { :name => "set" }, # :limit => 64
121
- :char => { :name => "char" }, # :limit => 255
122
- }
123
-
124
- # @override
125
- def native_database_types
126
- NATIVE_DATABASE_TYPES
127
- end
128
-
129
- ADAPTER_NAME = 'MySQL'.freeze
130
-
131
- # @override
132
- def adapter_name
133
- ADAPTER_NAME
134
- end
135
-
136
- def case_sensitive_equality_operator
137
- "= BINARY"
138
- end
139
-
140
- def case_sensitive_modifier(node)
141
- Arel::Nodes::Bin.new(node)
142
- end unless AR42
143
-
144
- def case_sensitive_modifier(node, table_attribute)
145
- node = Arel::Nodes.build_quoted node, table_attribute
146
- Arel::Nodes::Bin.new(node)
147
- end if AR42
148
-
149
- def case_sensitive_comparison(table, attribute, column, value)
150
- if column.case_sensitive?
151
- table[attribute].eq(value)
152
- else
153
- super
154
- end
155
- end if AR42
156
-
157
- def case_insensitive_comparison(table, attribute, column, value)
158
- if column.case_sensitive?
159
- super
160
- else
161
- table[attribute].eq(value)
162
- end
163
- end if AR42
164
-
165
- def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
166
- where_sql
167
- end
168
-
169
- def initialize_schema_migrations_table
170
- if @config[:encoding] == 'utf8mb4'
171
- ActiveRecord::SchemaMigration.create_table(191)
172
- else
173
- ActiveRecord::SchemaMigration.create_table
174
- end
175
- end if AR40
176
-
177
- # HELPER METHODS ===========================================
178
-
179
- # @private Only for Rails core compatibility.
180
- def new_column(field, default, type, null, collation, extra = "")
181
- jdbc_column_class.new(field, default, type, null, collation, strict_mode?, extra)
182
- end unless AR42
183
-
184
- # @private Only for Rails core compatibility.
185
- def new_column(field, default, cast_type, sql_type = nil, null = true, collation = "", extra = "")
186
- jdbc_column_class.new(field, default, cast_type, sql_type, null, collation, strict_mode?, extra)
187
- end if AR42
188
-
189
- # @private Only for Rails core compatibility.
190
- def error_number(exception)
191
- exception.error_code if exception.respond_to?(:error_code)
192
- end
193
-
194
- # QUOTING ==================================================
195
-
196
- # @override
197
- def quote(value, column = nil)
198
- return value.quoted_id if value.respond_to?(:quoted_id)
199
- return value if sql_literal?(value)
200
- return value.to_s if column && column.type == :primary_key
201
-
202
- if value.kind_of?(String) && column && column.type == :binary
203
- "x'#{value.unpack("H*")[0]}'"
204
- elsif value.kind_of?(BigDecimal)
205
- value.to_s("F")
206
- else
207
- super
208
- end
209
- end unless AR42
210
-
211
- # @private since AR 4.2
212
- def _quote(value)
213
- if value.is_a?(Type::Binary::Data)
214
- "x'#{value.hex}'"
215
- else
216
- super
217
- end
218
- end if AR42
219
-
220
- # @override
221
- def quote_column_name(name)
222
- "`#{name.to_s.gsub('`', '``')}`"
223
- end
224
-
225
- # @override
226
- def quote_table_name(name)
227
- quote_column_name(name).gsub('.', '`.`')
228
- end
229
-
230
- # @override
231
- def supports_migrations?
232
- true
233
- end
234
-
235
- # @override
236
- def supports_primary_key?
237
- true
238
- end
239
-
240
- # @override
241
- def supports_index_sort_order?
242
- # Technically MySQL allows to create indexes with the sort order syntax
243
- # but at the moment (5.5) it doesn't yet implement them.
244
- true
245
- end
246
-
247
- # @override
248
- def supports_indexes_in_create?
249
- true
250
- end
251
-
252
- # @override
253
- def supports_transaction_isolation?
254
- # MySQL 4 technically support transaction isolation, but it is affected by
255
- # a bug where the transaction level gets persisted for the whole session:
256
- # http://bugs.mysql.com/bug.php?id=39170
257
- version[0] && version[0] >= 5
258
- end
259
-
260
- # @override
261
- def supports_views?
262
- version[0] && version[0] >= 5
263
- end
264
-
265
- def supports_rename_index?
266
- return false if mariadb? || ! version[0]
267
- (version[0] == 5 && version[1] >= 7) || version[0] >= 6
268
- end
269
-
270
- def index_algorithms
271
- { :default => 'ALGORITHM = DEFAULT', :copy => 'ALGORITHM = COPY', :inplace => 'ALGORITHM = INPLACE' }
272
- end if AR42
273
-
274
- # @override
275
- def supports_transaction_isolation?(level = nil)
276
- version[0] && version[0] >= 5 # MySQL 5+
277
- end
278
-
279
- # NOTE: handled by JdbcAdapter only to have statements in logs :
280
-
281
- # @override
282
- def supports_savepoints?
283
- true
284
- end
285
-
286
- # @override
287
- def create_savepoint(name = current_savepoint_name(true))
288
- log("SAVEPOINT #{name}", 'Savepoint') { super }
289
- end
290
-
291
- # @override
292
- def rollback_to_savepoint(name = current_savepoint_name(true))
293
- log("ROLLBACK TO SAVEPOINT #{name}", 'Savepoint') { super }
294
- end
295
-
296
- # @override
297
- def release_savepoint(name = current_savepoint_name(false))
298
- log("RELEASE SAVEPOINT #{name}", 'Savepoint') { super }
299
- end
300
-
301
- def disable_referential_integrity
302
- fk_checks = select_value("SELECT @@FOREIGN_KEY_CHECKS")
303
- begin
304
- update("SET FOREIGN_KEY_CHECKS = 0")
305
- yield
306
- ensure
307
- update("SET FOREIGN_KEY_CHECKS = #{fk_checks}")
308
- end
309
- end
310
-
311
- # @override make it public just like native MySQL adapter does
312
- def update_sql(sql, name = nil)
313
- super
314
- end
315
-
316
- # SCHEMA STATEMENTS ========================================
317
-
318
- # @deprecated no longer used - handled with (AR built-in) Rake tasks
319
- def structure_dump
320
- # NOTE: due AR (2.3-3.2) compatibility views are not included
321
- if supports_views?
322
- sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
323
- else
324
- sql = "SHOW TABLES"
325
- end
326
-
327
- @connection.execute_query_raw(sql).map do |table|
328
- # e.g. { "Tables_in_arjdbc_test"=>"big_fields", "Table_type"=>"BASE TABLE" }
329
- table.delete('Table_type')
330
- table_name = table.to_a.first.last
331
-
332
- create_table = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")
333
-
334
- "#{create_table['Create Table']};\n\n"
335
- end.join
336
- end
337
-
338
- # Returns just a table's primary key.
339
- # @override
340
- def primary_key(table)
341
- #pk_and_sequence = pk_and_sequence_for(table)
342
- #pk_and_sequence && pk_and_sequence.first
343
- @connection.primary_keys(table).first
344
- end
345
-
346
- # Returns a table's primary key and belonging sequence.
347
- # @note Not used, only here for potential compatibility with native adapter.
348
- # @override
349
- def pk_and_sequence_for(table)
350
- result = execute("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA').first
351
- if result['Create Table'].to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
352
- keys = $1.split(","); keys.map! { |key| key.gsub(/[`"]/, "") }
353
- return keys.length == 1 ? [ keys.first, nil ] : nil
354
- else
355
- return nil
356
- end
357
- end
358
-
359
- # @private
360
- IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition
361
-
362
- INDEX_TYPES = [ :fulltext, :spatial ] if AR40
363
- INDEX_USINGS = [ :btree, :hash ] if AR40
364
-
365
- # Returns an array of indexes for the given table.
366
- # @override
367
- def indexes(table_name, name = nil)
368
- indexes = []
369
- current_index = nil
370
- result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name || 'SCHEMA')
371
- result.each do |row|
372
- key_name = row['Key_name']
373
- if current_index != key_name
374
- next if key_name == 'PRIMARY' # skip the primary key
375
- current_index = key_name
376
- indexes <<
377
- if self.class.const_defined?(:INDEX_TYPES) # AR 4.0
378
- mysql_index_type = row['Index_type'].downcase.to_sym
379
- index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
380
- index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
381
- IndexDefinition.new(row['Table'], key_name, row['Non_unique'].to_i == 0, [], [], nil, nil, index_type, index_using)
382
- else
383
- IndexDefinition.new(row['Table'], key_name, row['Non_unique'].to_i == 0, [], [])
384
- end
385
- end
386
-
387
- indexes.last.columns << row["Column_name"]
388
- indexes.last.lengths << row["Sub_part"]
389
- end
390
- indexes
391
- end
392
-
393
- # Returns an array of `Column` objects for the table specified.
394
- # @override
395
- def columns(table_name, name = nil)
396
- sql = "SHOW FULL #{AR40 ? 'FIELDS' : 'COLUMNS'} FROM #{quote_table_name(table_name)}"
397
- columns = execute(sql, name || 'SCHEMA')
398
- strict = strict_mode?
399
- pass_cast_type = respond_to?(:lookup_cast_type)
400
- columns.map! do |field|
401
- sql_type = field['Type']
402
- null = field['Null'] == "YES"
403
- if pass_cast_type
404
- cast_type = lookup_cast_type(sql_type)
405
- jdbc_column_class.new(field['Field'], field['Default'], cast_type, sql_type, null, field['Collation'], strict, field['Extra'])
406
- else
407
- jdbc_column_class.new(field['Field'], field['Default'], sql_type, null, field['Collation'], strict, field['Extra'])
408
- end
409
- end
410
- columns
411
- end
412
-
413
- if defined? ::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation
414
-
415
- class SchemaCreation < ::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation
416
-
417
- # @private
418
- def visit_AddColumn(o)
419
- add_column_position!(super, column_options(o))
420
- end
421
-
422
- # @private re-defined since AR 4.1
423
- def visit_ChangeColumnDefinition(o)
424
- column = o.column
425
- options = o.options
426
- sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
427
- change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
428
- add_column_options!(change_column_sql, options.merge(:column => column))
429
- add_column_position!(change_column_sql, options)
430
- end
431
-
432
- # @private since AR 4.2
433
- def visit_DropForeignKey(name)
434
- "DROP FOREIGN KEY #{name}"
435
- end
436
-
437
- # @private since AR 4.2
438
- def visit_TableDefinition(o)
439
- name = o.name
440
- create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
441
-
442
- statements = o.columns.map { |c| accept c }
443
- statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
444
-
445
- create_sql << "(#{statements.join(', ')}) " if statements.present?
446
- create_sql << "#{o.options}"
447
- create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
448
- create_sql
449
- end if AR42
450
-
451
- private
452
-
453
- def add_column_position!(sql, options)
454
- if options[:first]
455
- sql << " FIRST"
456
- elsif options[:after]
457
- sql << " AFTER #{quote_column_name(options[:after])}"
458
- end
459
- sql
460
- end
461
-
462
- def column_options(o)
463
- column_options = {}
464
- column_options[:null] = o.null unless o.null.nil?
465
- column_options[:default] = o.default unless o.default.nil?
466
- column_options[:column] = o
467
- column_options[:first] = o.first
468
- column_options[:after] = o.after
469
- column_options
470
- end
471
-
472
- def index_in_create(table_name, column_name, options)
473
- index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
474
- "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
475
- end
476
-
477
- end
478
-
479
- def schema_creation; SchemaCreation.new self end
480
-
481
- end
482
-
483
- # @private
484
- def recreate_database(name, options = {})
485
- drop_database(name)
486
- create_database(name, options)
487
- reconnect!
488
- end
489
-
490
- # @override
491
- def create_database(name, options = {})
492
- if options[:collation]
493
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
494
- else
495
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
496
- end
497
- end
498
-
499
- # @override
500
- def drop_database(name)
501
- execute "DROP DATABASE IF EXISTS `#{name}`"
502
- end
503
-
504
- def current_database
505
- select_one("SELECT DATABASE() as db")['db']
506
- end
507
-
508
- def truncate(table_name, name = nil)
509
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
510
- end
511
-
512
- # @override
513
- def create_table(name, options = {})
514
- super(name, { :options => "ENGINE=InnoDB" }.merge(options))
515
- end
516
-
517
- def drop_table(table_name, options = {})
518
- execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}"
519
- end
520
-
521
- # @override
522
- def rename_table(table_name, new_name)
523
- execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
524
- rename_table_indexes(table_name, new_name) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements
525
- end
526
-
527
- # @override
528
- def remove_index!(table_name, index_name)
529
- # missing table_name quoting in AR-2.3
530
- execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
531
- end
532
-
533
- # @override
534
- def rename_index(table_name, old_name, new_name)
535
- if supports_rename_index?
536
- validate_index_length!(table_name, new_name) if respond_to?(:validate_index_length!)
537
- execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
538
- else
539
- super
540
- end
541
- end
542
-
543
- # @private
544
- ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition if ::ActiveRecord::ConnectionAdapters.const_defined? :ForeignKeyDefinition
545
-
546
- # @override
547
- def supports_foreign_keys?; true end
548
-
549
- def foreign_keys(table_name)
550
- fk_info = select_all "" <<
551
- "SELECT fk.referenced_table_name as 'to_table' " <<
552
- ",fk.referenced_column_name as 'primary_key' " <<
553
- ",fk.column_name as 'column' " <<
554
- ",fk.constraint_name as 'name' " <<
555
- "FROM information_schema.key_column_usage fk " <<
556
- "WHERE fk.referenced_column_name is not null " <<
557
- "AND fk.table_schema = '#{current_database}' " <<
558
- "AND fk.table_name = '#{table_name}'"
559
-
560
- create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
561
-
562
- fk_info.map! do |row|
563
- options = {
564
- :column => row['column'], :name => row['name'], :primary_key => row['primary_key']
565
- }
566
- options[:on_update] = extract_foreign_key_action(create_table_info, row['name'], "UPDATE")
567
- options[:on_delete] = extract_foreign_key_action(create_table_info, row['name'], "DELETE")
568
-
569
- ForeignKeyDefinition.new(table_name, row['to_table'], options)
570
- end
571
- end if defined? ForeignKeyDefinition
572
-
573
- def extract_foreign_key_action(structure, name, action)
574
- if structure =~ /CONSTRAINT #{quote_column_name(name)} FOREIGN KEY .* REFERENCES .* ON #{action} (CASCADE|SET NULL|RESTRICT)/
575
- case $1
576
- when 'CASCADE'; :cascade
577
- when 'SET NULL'; :nullify
578
- end
579
- end
12
+ module ActiveRecord
13
+ class ConnectionAdapters::AbstractMysqlAdapter
14
+ # FIXME: this is to work around abstract mysql having 4 arity but core wants to pass 3
15
+ # FIXME: Missing the logic from original module.
16
+ def initialize(connection, logger, config)
17
+ super(connection, logger, config)
580
18
  end
581
- private :extract_foreign_key_action
582
-
583
- # @override
584
- def add_column(table_name, column_name, type, options = {})
585
- add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
586
- add_column_options!(add_column_sql, options)
587
- add_column_position!(add_column_sql, options)
588
- execute(add_column_sql)
589
- end unless const_defined? :SchemaCreation
590
-
591
- def change_column_default(table_name, column_name, default)
592
- column = column_for(table_name, column_name)
593
- change_column table_name, column_name, column.sql_type, :default => default
594
- end # unless const_defined? :SchemaCreation
19
+ end
20
+ module ConnectionAdapters
21
+ # Remove any vestiges of core/Ruby MySQL adapter
22
+ remove_const(:Mysql2Adapter) if const_defined?(:Mysql2Adapter)
595
23
 
596
- def change_column_null(table_name, column_name, null, default = nil)
597
- column = column_for(table_name, column_name)
24
+ class Mysql2Adapter < AbstractMysqlAdapter
25
+ ADAPTER_NAME = 'Mysql2'.freeze
598
26
 
599
- unless null || default.nil?
600
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
601
- end
27
+ include ArJdbc::Abstract::Core
28
+ include ArJdbc::Abstract::ConnectionManagement
29
+ include ArJdbc::Abstract::DatabaseStatements
30
+ include ArJdbc::Abstract::StatementCache
31
+ include ArJdbc::Abstract::TransactionSupport
602
32
 
603
- change_column table_name, column_name, column.sql_type, :null => null
604
- end # unless const_defined? :SchemaCreation
33
+ def initialize(connection, logger, connection_options, config)
34
+ super(connection, logger, config)
605
35
 
606
- # @override
607
- def change_column(table_name, column_name, type, options = {})
608
- column = column_for(table_name, column_name)
36
+ @prepared_statements = false unless config.key?(:prepared_statements)
609
37
 
610
- unless options_include_default?(options)
611
- # NOTE: no defaults for BLOB/TEXT columns with MySQL
612
- options[:default] = column.default if type != :text && type != :binary
38
+ configure_connection
613
39
  end
614
40
 
615
- unless options.has_key?(:null)
616
- options[:null] = column.null
41
+ def supports_json?
42
+ !mariadb? && version >= '5.7.8'
617
43
  end
618
44
 
619
- change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
620
- add_column_options!(change_column_sql, options)
621
- add_column_position!(change_column_sql, options)
622
- execute(change_column_sql)
623
- end
624
-
625
- # @private
626
- def change_column(table_name, column_name, type, options = {})
627
- execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}")
628
- end if AR42
629
-
630
- # @override
631
- def rename_column(table_name, column_name, new_column_name)
632
- options = {}
633
-
634
- if column = columns(table_name).find { |c| c.name == column_name.to_s }
635
- type = column.type
636
- options[:default] = column.default if type != :text && type != :binary
637
- options[:null] = column.null
638
- else
639
- raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
640
- end
641
-
642
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
643
-
644
- rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
645
- add_column_options!(rename_column_sql, options)
646
- execute(rename_column_sql)
647
- rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements
648
- end
649
-
650
- def add_column_position!(sql, options)
651
- if options[:first]
652
- sql << " FIRST"
653
- elsif options[:after]
654
- sql << " AFTER #{quote_column_name(options[:after])}"
45
+ def supports_comments?
46
+ true
655
47
  end
656
- end unless const_defined? :SchemaCreation
657
48
 
658
- # @note Only used with (non-AREL) ActiveRecord **2.3**.
659
- # @see Arel::Visitors::MySQL
660
- def add_limit_offset!(sql, options)
661
- limit, offset = options[:limit], options[:offset]
662
- if limit && offset
663
- sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
664
- elsif limit
665
- sql << " LIMIT #{sanitize_limit(limit)}"
666
- elsif offset
667
- sql << " OFFSET #{offset.to_i}"
49
+ def supports_comments_in_create?
50
+ true
668
51
  end
669
- sql
670
- end if ::ActiveRecord::VERSION::MAJOR < 3
671
-
672
- # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
673
- # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
674
- # these, we must use a subquery. However, MySQL is too stupid to create a
675
- # temporary table for this automatically, so we have to give it some prompting
676
- # in the form of a subsubquery. Ugh!
677
- # @private based on mysql_adapter.rb from 3.1-stable
678
- def join_to_update(update, select)
679
- if select.limit || select.offset || select.orders.any?
680
- subsubselect = select.clone
681
- subsubselect.projections = [update.key]
682
52
 
683
- subselect = Arel::SelectManager.new(select.engine)
684
- subselect.project Arel.sql(update.key.name)
685
- subselect.from subsubselect.as('__active_record_temp')
686
-
687
- update.where update.key.in(subselect)
688
- else
689
- update.table select.source
690
- update.wheres = select.constraints
53
+ def supports_savepoints?
54
+ true
691
55
  end
692
- end
693
-
694
- def show_variable(var)
695
- res = execute("show variables like '#{var}'")
696
- result_row = res.detect {|row| row["Variable_name"] == var }
697
- result_row && result_row["Value"]
698
- end
699
-
700
- def charset
701
- show_variable("character_set_database")
702
- end
703
-
704
- def collation
705
- show_variable("collation_database")
706
- end
707
56
 
708
- # Maps logical Rails types to MySQL-specific data types.
709
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
710
- case type.to_s
711
- when 'binary'
712
- case limit
713
- when 0..0xfff; "varbinary(#{limit})"
714
- when nil; "blob"
715
- when 0x1000..0xffffffff; "blob(#{limit})"
716
- else raise(ActiveRecordError, "No binary type has character length #{limit}")
717
- end
718
- when 'integer'
719
- case limit
720
- when 1; 'tinyint'
721
- when 2; 'smallint'
722
- when 3; 'mediumint'
723
- when nil, 4, 11; 'int(11)' # compatibility with MySQL default
724
- when 5..8; 'bigint'
725
- else raise(ActiveRecordError, "No integer type has byte size #{limit}")
726
- end
727
- when 'text'
728
- case limit
729
- when 0..0xff; 'tinytext'
730
- when nil, 0x100..0xffff; 'text'
731
- when 0x10000..0xffffff; 'mediumtext'
732
- when 0x1000000..0xffffffff; 'longtext'
733
- else raise(ActiveRecordError, "No text type has character length #{limit}")
734
- end
735
- when 'datetime'
736
- return super unless precision
57
+ # HELPER METHODS ===========================================
737
58
 
738
- case precision
739
- when 0..6; "datetime(#{precision})"
740
- else raise(ActiveRecordError, "No datetime type has precision of #{precision}. The allowed range of precision is from 0 to 6.")
741
- end
742
- else
59
+ # Reloading the type map in abstract/statement_cache.rb blows up postgres
60
+ def clear_cache!
61
+ reload_type_map
743
62
  super
744
63
  end
745
- end
746
-
747
- # @override
748
- def empty_insert_statement_value
749
- "VALUES ()"
750
- end
751
-
752
- # @note since AR 4.2
753
- def valid_type?(type)
754
- ! native_database_types[type].nil?
755
- end
756
-
757
- def clear_cache!
758
- super
759
- reload_type_map
760
- end if AR42
761
-
762
- # @private since AR 4.2
763
- def prepare_column_options(column, types)
764
- spec = super
765
- spec.delete(:limit) if column.type == :boolean
766
- spec
767
- end if AR42
768
-
769
- # @private
770
- Type = ActiveRecord::Type if AR42
771
-
772
- protected
773
-
774
- # @private
775
- def initialize_type_map(m)
776
- super
777
-
778
- register_class_with_limit m, %r(char)i, MysqlString
779
-
780
- m.register_type %r(tinytext)i, Type::Text.new(:limit => 2**8 - 1)
781
- m.register_type %r(tinyblob)i, Type::Binary.new(:limit => 2**8 - 1)
782
- m.register_type %r(text)i, Type::Text.new(:limit => 2**16 - 1)
783
- m.register_type %r(blob)i, Type::Binary.new(:limit => 2**16 - 1)
784
- m.register_type %r(mediumtext)i, Type::Text.new(:limit => 2**24 - 1)
785
- m.register_type %r(mediumblob)i, Type::Binary.new(:limit => 2**24 - 1)
786
- m.register_type %r(longtext)i, Type::Text.new(:limit => 2**32 - 1)
787
- m.register_type %r(longblob)i, Type::Binary.new(:limit => 2**32 - 1)
788
- m.register_type %r(^float)i, Type::Float.new(:limit => 24)
789
- m.register_type %r(^double)i, Type::Float.new(:limit => 53)
790
-
791
- register_integer_type m, %r(^bigint)i, :limit => 8
792
- register_integer_type m, %r(^int)i, :limit => 4
793
- register_integer_type m, %r(^mediumint)i, :limit => 3
794
- register_integer_type m, %r(^smallint)i, :limit => 2
795
- register_integer_type m, %r(^tinyint)i, :limit => 1
796
64
 
797
- m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
798
- m.alias_type %r(set)i, 'varchar'
799
- m.alias_type %r(year)i, 'integer'
800
- m.alias_type %r(bit)i, 'binary'
801
-
802
- m.register_type(%r(datetime)i) do |sql_type|
803
- precision = extract_precision(sql_type)
804
- MysqlDateTime.new(:precision => precision)
805
- end
806
-
807
- m.register_type(%r(enum)i) do |sql_type|
808
- limit = sql_type[/^enum\((.+)\)/i, 1].split(',').
809
- map{|enum| enum.strip.length - 2}.max
810
- MysqlString.new(:limit => limit)
811
- end
812
- end if AR42
813
-
814
- # @private
815
- def register_integer_type(mapping, key, options)
816
- mapping.register_type(key) do |sql_type|
817
- if /unsigned/i =~ sql_type
818
- Type::UnsignedInteger.new(options)
65
+ def each_hash(result) # :nodoc:
66
+ if block_given?
67
+ # FIXME: This is C in mysql2 gem and I just made simplest Ruby
68
+ result.each do |row|
69
+ new_hash = {}
70
+ row.each { |k, v| new_hash[k.to_sym] = v }
71
+ yield new_hash
72
+ end
819
73
  else
820
- Type::Integer.new(options)
74
+ to_enum(:each_hash, result)
821
75
  end
822
76
  end
823
- end if AR42
824
-
825
- # MySQL is too stupid to create a temporary table for use subquery, so we have
826
- # to give it some prompting in the form of a subsubquery. Ugh!
827
- # @note since AR 4.2
828
- def subquery_for(key, select)
829
- subsubselect = select.clone
830
- subsubselect.projections = [key]
831
-
832
- subselect = Arel::SelectManager.new(select.engine)
833
- subselect.project Arel.sql(key.name)
834
- subselect.from subsubselect.as('__active_record_temp')
835
- end if AR42
836
-
837
- def quoted_columns_for_index(column_names, options = {})
838
- length = options[:length] if options.is_a?(Hash)
839
-
840
- case length
841
- when Hash
842
- column_names.map { |name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
843
- when Fixnum
844
- column_names.map { |name| "#{quote_column_name(name)}(#{length})" }
845
- else
846
- column_names.map { |name| quote_column_name(name) }
847
- end
848
- end
849
77
 
850
- # @override
851
- def translate_exception(exception, message)
852
- return super unless exception.respond_to?(:errno)
853
-
854
- case exception.errno
855
- when 1062
856
- ::ActiveRecord::RecordNotUnique.new(message, exception)
857
- when 1452
858
- ::ActiveRecord::InvalidForeignKey.new(message, exception)
859
- else
860
- super
78
+ def error_number(exception)
79
+ exception.errno if exception.respond_to? :errno
861
80
  end
862
- end
863
-
864
- private
865
81
 
866
- def column_for(table_name, column_name)
867
- unless column = columns(table_name).find { |c| c.name == column_name.to_s }
868
- raise "No such column: #{table_name}.#{column_name}"
82
+ # FIXME: #833 This is wrong...it should not always pass utf8 and native adapter never does...
83
+ def create_table(table_name, **options) #:nodoc:
84
+ super(table_name, options: 'ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci', **options)
869
85
  end
870
- column
871
- end
872
86
 
873
- def mariadb?; !! ( full_version =~ /mariadb/i ) end
87
+ #--
88
+ # QUOTING ==================================================
89
+ #+
874
90
 
875
- def version
876
- return @version ||= begin
877
- version = []
878
- java_connection = jdbc_connection(true)
879
- if java_connection.java_class.name == 'com.mysql.jdbc.ConnectionImpl'
880
- version << jdbc_connection.serverMajorVersion
881
- version << jdbc_connection.serverMinorVersion
882
- version << jdbc_connection.serverSubMinorVersion
883
- else
884
- if match = full_version.match(/^(\d+)\.(\d+)\.(\d+)/)
885
- version << match[1].to_i
886
- version << match[2].to_i
887
- version << match[3].to_i
888
- end
889
- end
890
- version.freeze
91
+ # FIXME: 5.1 crashes without this. I think this is Arel hitting a fallback path in to_sql.rb.
92
+ # So maybe an untested code path in their source. Still means we are doing something wrong to
93
+ # even hit it.
94
+ def quote(value, comment=nil)
95
+ super(value)
891
96
  end
892
- end
893
97
 
894
- def full_version
895
- @full_version ||= begin
896
- result = execute 'SELECT VERSION()', 'SCHEMA'
897
- result.first.values.first # [{"VERSION()"=>"5.5.37-0ubuntu..."}]
98
+ def quote_string(string)
99
+ string.gsub(/[\x00\n\r\\\'\"]/, '\\\\\0')
898
100
  end
899
- end
900
-
901
- # @private
902
- def emulate_booleans; ::ArJdbc::MySQL.emulate_booleans?; end # due AR 4.2
903
- public :emulate_booleans
904
-
905
- # @private
906
- class MysqlDateTime < Type::DateTime
907
- private
908
101
 
909
- def has_precision?
910
- precision || 0
911
- end
912
- end if AR42
913
-
914
- # @private
915
- class MysqlString < Type::String
916
- def type_cast_for_database(value)
917
- case value
918
- when true then "1"
919
- when false then "0"
920
- else super
921
- end
102
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
103
+ last_id = if without_prepared_statement?(binds)
104
+ log(sql, name) { @connection.execute_insert(sql) }
105
+ else
106
+ log(sql, name, binds) { @connection.execute_insert(sql, binds) }
107
+ end
108
+ # FIXME: execute_insert and executeUpdate mapping key results is very varied and I am wondering
109
+ # if AR is now much more consistent. I worked around by manually making a result here.
110
+ ::ActiveRecord::Result.new(nil, [[last_id]])
922
111
  end
112
+ alias insert_sql exec_insert
113
+ deprecate insert_sql: :insert
923
114
 
924
115
  private
925
116
 
926
- def cast_value(value)
927
- case value
928
- when true then "1"
929
- when false then "0"
930
- else super
117
+ def full_version
118
+ @full_version ||= begin
119
+ result = execute 'SELECT VERSION()', 'SCHEMA'
120
+ result.first.values.first # [{"VERSION()"=>"5.5.37-0ubuntu..."}]
931
121
  end
932
122
  end
933
- end if AR42
934
-
935
- end
936
- end
937
-
938
- module ActiveRecord
939
- module ConnectionAdapters
940
- # Remove any vestiges of core/Ruby MySQL adapter
941
- remove_const(:MysqlAdapter) if const_defined?(:MysqlAdapter)
942
-
943
- class MysqlAdapter < JdbcAdapter
944
- include ::ArJdbc::MySQL
945
- include ::ArJdbc::MySQL::ExplainSupport
946
-
947
- def arel_visitor # :nodoc:
948
- Arel::Visitors::MySQL.new(self)
949
- end
950
-
951
- # By default, the MysqlAdapter will consider all columns of type
952
- # __tinyint(1)__ as boolean. If you wish to disable this :
953
- # ```
954
- # ActiveRecord::ConnectionAdapters::Mysql[2]Adapter.emulate_booleans = false
955
- # ```
956
- def self.emulate_booleans?; ::ArJdbc::MySQL.emulate_booleans?; end
957
- def self.emulate_booleans; ::ArJdbc::MySQL.emulate_booleans?; end # native adapter
958
- def self.emulate_booleans=(emulate); ::ArJdbc::MySQL.emulate_booleans = emulate; end
959
-
960
- class Column < JdbcColumn
961
- include ::ArJdbc::MySQL::Column
962
-
963
- # @note {#ArJdbc::MySQL::Column} uses this to check for boolean emulation
964
- def adapter
965
- MysqlAdapter
966
- end
967
-
968
- end
969
-
970
- #def initialize(*args)
971
- # super # configure_connection happens in super
972
- #end
973
123
 
974
124
  def jdbc_connection_class(spec)
975
- ::ArJdbc::MySQL.jdbc_connection_class
125
+ ::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
976
126
  end
977
127
 
978
128
  def jdbc_column_class
979
- Column
129
+ ::ActiveRecord::ConnectionAdapters::MySQL::Column
980
130
  end
981
131
 
982
132
  end
133
+ end
983
134
 
984
- if ActiveRecord::VERSION::MAJOR < 3 ||
985
- ( ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR <= 1 )
986
- remove_const(:MysqlColumn) if const_defined?(:MysqlColumn)
987
- MysqlColumn = MysqlAdapter::Column
988
- end
989
-
990
- if ActiveRecord::VERSION::MAJOR > 3 ||
991
- ( ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR >= 1 )
992
- remove_const(:Mysql2Adapter) if const_defined?(:Mysql2Adapter)
993
- Mysql2Adapter = MysqlAdapter
994
- if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 1
995
- remove_const(:Mysql2Column) if const_defined?(:Mysql2Column)
996
- Mysql2Column = MysqlAdapter::Column
135
+ # FIXME: #834 Not sure how this is scoped or whether we should use it or just alias it to our
136
+ # JDBCError.
137
+ class ::Mysql2
138
+ class Error < Exception
139
+ def initialize(*)
140
+ super("error")
997
141
  end
998
142
  end
999
-
1000
143
  end
1001
144
  end