activerecord-jdbc-adapter 1.2.5 → 1.2.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +5 -1
- data/Appraisals +5 -5
- data/Gemfile +9 -1
- data/Gemfile.lock +44 -10
- data/History.txt +126 -2
- data/README.md +246 -0
- data/Rakefile +34 -25
- data/activerecord-jdbc-adapter.gemspec +1 -1
- data/gemfiles/rails23.gemfile +5 -3
- data/gemfiles/rails23.gemfile.lock +26 -18
- data/gemfiles/rails30.gemfile +4 -2
- data/gemfiles/rails30.gemfile.lock +16 -8
- data/gemfiles/rails31.gemfile +4 -2
- data/gemfiles/rails31.gemfile.lock +16 -9
- data/gemfiles/rails32.gemfile +4 -2
- data/gemfiles/rails32.gemfile.lock +15 -8
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
- data/lib/arel/visitors/sql_server.rb +3 -0
- data/lib/arjdbc.rb +3 -5
- data/lib/arjdbc/db2.rb +1 -0
- data/lib/arjdbc/db2/adapter.rb +302 -196
- data/lib/arjdbc/db2/connection_methods.rb +18 -0
- data/lib/arjdbc/derby/active_record_patch.rb +12 -0
- data/lib/arjdbc/derby/adapter.rb +180 -158
- data/lib/arjdbc/derby/connection_methods.rb +5 -1
- data/lib/arjdbc/firebird/adapter.rb +27 -19
- data/lib/arjdbc/h2/adapter.rb +162 -7
- data/lib/arjdbc/h2/connection_methods.rb +5 -1
- data/lib/arjdbc/hsqldb.rb +1 -1
- data/lib/arjdbc/hsqldb/adapter.rb +96 -61
- data/lib/arjdbc/hsqldb/connection_methods.rb +5 -1
- data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
- data/lib/arjdbc/informix/adapter.rb +56 -55
- data/lib/arjdbc/jdbc/adapter.rb +173 -86
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/column.rb +28 -23
- data/lib/arjdbc/jdbc/connection.rb +10 -6
- data/lib/arjdbc/jdbc/driver.rb +13 -5
- data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +21 -0
- data/lib/arjdbc/mssql.rb +1 -1
- data/lib/arjdbc/mssql/adapter.rb +51 -53
- data/lib/arjdbc/mssql/connection_methods.rb +8 -1
- data/lib/arjdbc/mysql.rb +1 -1
- data/lib/arjdbc/mysql/adapter.rb +186 -150
- data/lib/arjdbc/mysql/connection_methods.rb +9 -9
- data/lib/arjdbc/mysql/explain_support.rb +85 -0
- data/lib/arjdbc/oracle.rb +1 -1
- data/lib/arjdbc/oracle/adapter.rb +232 -125
- data/lib/arjdbc/oracle/connection_methods.rb +2 -2
- data/lib/arjdbc/postgresql.rb +1 -1
- data/lib/arjdbc/postgresql/adapter.rb +134 -86
- data/lib/arjdbc/postgresql/connection_methods.rb +6 -4
- data/lib/arjdbc/postgresql/explain_support.rb +55 -0
- data/lib/arjdbc/sqlite3.rb +1 -1
- data/lib/arjdbc/sqlite3/adapter.rb +176 -108
- data/lib/arjdbc/sqlite3/connection_methods.rb +5 -5
- data/lib/arjdbc/sqlite3/explain_support.rb +32 -0
- data/lib/arjdbc/sybase/adapter.rb +7 -6
- data/lib/arjdbc/version.rb +1 -1
- data/pom.xml +1 -1
- data/rakelib/02-test.rake +9 -11
- data/rakelib/rails.rake +18 -10
- data/src/java/arjdbc/db2/DB2Module.java +70 -0
- data/src/java/arjdbc/derby/DerbyModule.java +24 -5
- data/src/java/arjdbc/hsqldb/HSQLDBModule.java +66 -0
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +14 -7
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +111 -89
- data/src/java/arjdbc/mysql/MySQLModule.java +79 -70
- data/src/java/arjdbc/oracle/OracleModule.java +74 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +5 -10
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +77 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +127 -0
- data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +25 -111
- data/src/java/arjdbc/util/QuotingUtils.java +104 -0
- data/test/abstract_db_create.rb +6 -6
- data/test/activerecord/connection_adapters/type_conversion_test.rb +2 -2
- data/test/assets/flowers.jpg +0 -0
- data/test/binary.rb +67 -0
- data/test/db/db2.rb +30 -7
- data/test/db/jdbc.rb +4 -2
- data/test/db/oracle.rb +18 -27
- data/test/db2_binary_test.rb +6 -0
- data/test/db2_serialize_test.rb +6 -0
- data/test/db2_simple_test.rb +20 -25
- data/test/db2_test.rb +71 -0
- data/test/derby_binary_test.rb +6 -0
- data/test/derby_migration_test.rb +42 -35
- data/test/derby_reset_column_information_test.rb +1 -0
- data/test/derby_row_locking_test.rb +17 -0
- data/test/derby_schema_dump_test.rb +9 -0
- data/test/derby_serialize_test.rb +6 -0
- data/test/derby_simple_test.rb +59 -17
- data/test/generic_jdbc_connection_test.rb +112 -5
- data/test/h2_binary_test.rb +6 -0
- data/test/h2_change_column_test.rb +1 -1
- data/test/h2_schema_dump_test.rb +25 -0
- data/test/h2_serialize_test.rb +6 -0
- data/test/h2_simple_test.rb +23 -9
- data/test/has_many_through.rb +18 -4
- data/test/hsqldb_binary_test.rb +6 -0
- data/test/hsqldb_schema_dump_test.rb +15 -0
- data/test/hsqldb_serialize_test.rb +6 -0
- data/test/hsqldb_simple_test.rb +1 -0
- data/test/informix_simple_test.rb +1 -1
- data/test/jdbc/db2.rb +23 -0
- data/test/jdbc/oracle.rb +23 -0
- data/test/jdbc_common.rb +3 -110
- data/test/jndi_callbacks_test.rb +0 -2
- data/test/jndi_test.rb +2 -0
- data/test/models/binary.rb +18 -0
- data/test/models/custom_pk_name.rb +1 -0
- data/test/models/data_types.rb +11 -2
- data/test/models/entry.rb +1 -1
- data/test/models/string_id.rb +2 -2
- data/test/models/thing.rb +1 -1
- data/test/models/topic.rb +32 -0
- data/test/mssql_legacy_types_test.rb +1 -1
- data/test/mssql_limit_offset_test.rb +13 -3
- data/test/mssql_serialize_test.rb +6 -0
- data/test/mysql_binary_test.rb +6 -0
- data/test/mysql_schema_dump_test.rb +220 -0
- data/test/mysql_serialize_test.rb +6 -0
- data/test/mysql_simple_test.rb +22 -2
- data/test/mysql_test.rb +93 -0
- data/test/oracle_binary_test.rb +6 -0
- data/test/oracle_limit_test.rb +2 -1
- data/test/oracle_serialize_test.rb +6 -0
- data/test/oracle_simple_test.rb +61 -0
- data/test/oracle_specific_test.rb +77 -26
- data/test/postgres_binary_test.rb +6 -0
- data/test/postgres_native_type_mapping_test.rb +12 -11
- data/test/postgres_nonseq_pkey_test.rb +1 -0
- data/test/postgres_reserved_test.rb +1 -0
- data/test/postgres_reset_column_information_test.rb +1 -0
- data/test/postgres_row_locking_test.rb +21 -0
- data/test/postgres_schema_dump_test.rb +88 -0
- data/test/postgres_schema_search_path_test.rb +1 -0
- data/test/postgres_simple_test.rb +62 -89
- data/test/postgres_table_alias_length_test.rb +1 -0
- data/test/postgres_test.rb +31 -0
- data/test/postgres_type_conversion_test.rb +16 -16
- data/test/row_locking.rb +69 -64
- data/test/schema_dump.rb +168 -0
- data/test/serialize.rb +277 -0
- data/test/simple.rb +326 -122
- data/test/sqlite3_serialize_test.rb +6 -0
- data/test/sqlite3_simple_test.rb +51 -84
- data/test/sqlite3_type_conversion_test.rb +101 -0
- data/test/test_helper.rb +224 -0
- metadata +325 -366
- data/README.rdoc +0 -214
- data/test/db/logger.rb +0 -3
- data/test/derby_multibyte_test.rb +0 -11
- data/test/mysql_info_test.rb +0 -123
Binary file
|
data/lib/arjdbc/jdbc/column.rb
CHANGED
@@ -5,43 +5,48 @@ module ActiveRecord
|
|
5
5
|
|
6
6
|
def initialize(config, name, default, *args)
|
7
7
|
call_discovered_column_callbacks(config)
|
8
|
-
super(name,default_value(default)
|
8
|
+
super(name, default_value(default), *args)
|
9
9
|
init_column(name, default, *args)
|
10
10
|
end
|
11
11
|
|
12
|
-
def init_column(*args)
|
13
|
-
|
12
|
+
def init_column(*args); end
|
13
|
+
|
14
|
+
def default_value(value); value; end
|
15
|
+
|
16
|
+
protected
|
14
17
|
|
15
|
-
def
|
16
|
-
|
18
|
+
def call_discovered_column_callbacks(config)
|
19
|
+
dialect = (config[:dialect] || config[:driver]).to_s
|
20
|
+
for matcher, block in self.class.column_types
|
21
|
+
block.call(config, self) if matcher === dialect
|
22
|
+
end
|
17
23
|
end
|
18
24
|
|
25
|
+
public
|
26
|
+
|
19
27
|
def self.column_types
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
28
|
+
# reset the column types if the # of constants changed since last call
|
29
|
+
@column_types ||= begin
|
30
|
+
types = driver_constants.select { |c| c.respond_to? :column_selector }
|
31
|
+
types.map! { |c| c.column_selector }
|
32
|
+
types.inject({}) { |h, val| h[ val[0] ] = val[1]; h }
|
25
33
|
end
|
26
|
-
@column_types ||= driver_constants.select {|c|
|
27
|
-
c.respond_to? :column_selector }.map {|c|
|
28
|
-
c.column_selector }.inject({}) {|h,val|
|
29
|
-
h[val[0]] = val[1]; h }
|
30
34
|
end
|
31
35
|
|
32
36
|
def self.driver_constants
|
33
|
-
|
37
|
+
reset_constants
|
38
|
+
@driver_constants ||= ::ArJdbc.constants.map { |c| ::ArJdbc.const_get c }
|
34
39
|
end
|
35
40
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
41
|
+
def self.reset_constants!
|
42
|
+
@driver_constants = nil; @column_types = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.reset_constants
|
46
|
+
return false if ! defined?(@driver_constants) || ! @driver_constants
|
47
|
+
reset_constants! if ::ArJdbc.constants.size != @driver_constants.size
|
44
48
|
end
|
49
|
+
|
45
50
|
end
|
46
51
|
end
|
47
52
|
end
|
@@ -41,7 +41,8 @@ module ActiveRecord
|
|
41
41
|
url = configure_url
|
42
42
|
username = config[:username].to_s
|
43
43
|
password = config[:password].to_s
|
44
|
-
jdbc_driver = ( config[:driver_instance] ||=
|
44
|
+
jdbc_driver = ( config[:driver_instance] ||=
|
45
|
+
JdbcDriver.new(config[:driver].to_s, config[:properties]) )
|
45
46
|
|
46
47
|
@connection_factory = JdbcConnectionFactory.impl do
|
47
48
|
jdbc_driver.connection(url, username, password)
|
@@ -84,11 +85,14 @@ module ActiveRecord
|
|
84
85
|
@stmts = {}
|
85
86
|
rescue ::ActiveRecord::ActiveRecordError
|
86
87
|
raise
|
87
|
-
rescue
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
}
|
88
|
+
rescue Java::JavaSql::SQLException => e
|
89
|
+
e = e.cause if defined?(NativeException) && e.is_a?(NativeException) # JRuby-1.6.8
|
90
|
+
error = e.getMessage || e.getSQLState
|
91
|
+
error = error ? "#{e.java_class.name}: #{error}" : e.java_class.name
|
92
|
+
error = ::ActiveRecord::JDBCError.new("The driver encountered an unknown error: #{error}")
|
93
|
+
error.errno = e.getErrorCode
|
94
|
+
error.sql_exception = e
|
95
|
+
raise error
|
92
96
|
end
|
93
97
|
|
94
98
|
def adapter=(adapter)
|
data/lib/arjdbc/jdbc/driver.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
3
|
class JdbcDriver
|
4
|
-
|
4
|
+
attr_reader :name, :properties
|
5
|
+
|
6
|
+
def initialize(name, properties = {})
|
5
7
|
@name = name
|
6
8
|
@driver = driver_class.new
|
9
|
+
if properties.is_a?(Java::JavaUtil::Properties)
|
10
|
+
@properties = properties # allow programmatically set properties
|
11
|
+
else
|
12
|
+
@properties = Java::JavaUtil::Properties.new
|
13
|
+
properties.each { |key, val| @properties[key] = val.to_s } if properties
|
14
|
+
end
|
7
15
|
end
|
8
16
|
|
9
17
|
def driver_class
|
@@ -25,10 +33,10 @@ module ActiveRecord
|
|
25
33
|
|
26
34
|
def connection(url, user, pass)
|
27
35
|
# bypass DriverManager to get around problem with dynamically loaded jdbc drivers
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@driver.connect(url,
|
36
|
+
properties = self.properties.clone
|
37
|
+
properties.setProperty("user", user) if user
|
38
|
+
properties.setProperty("password", pass) if pass
|
39
|
+
@driver.connect(url, properties)
|
32
40
|
end
|
33
41
|
end
|
34
42
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ArJdbc
|
2
|
+
module SerializedAttributesHelper
|
3
|
+
|
4
|
+
def self.dump_column_value(record, column)
|
5
|
+
value = record[ name = column.name.to_s ]
|
6
|
+
if record.class.respond_to?(:serialized_attributes)
|
7
|
+
if coder = record.class.serialized_attributes[name]
|
8
|
+
value = coder.respond_to?(:dump) ? coder.dump(value) : value.to_yaml
|
9
|
+
end
|
10
|
+
else
|
11
|
+
if record.respond_to?(:unserializable_attribute?)
|
12
|
+
value = value.to_yaml if record.unserializable_attribute?(name, column)
|
13
|
+
else
|
14
|
+
value = value.to_yaml if value.is_a?(Hash)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
value
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/arjdbc/mssql.rb
CHANGED
data/lib/arjdbc/mssql/adapter.rb
CHANGED
@@ -2,34 +2,34 @@ require 'strscan'
|
|
2
2
|
require 'arjdbc/mssql/tsql_helper'
|
3
3
|
require 'arjdbc/mssql/limit_helpers'
|
4
4
|
require 'arjdbc/mssql/lock_helpers'
|
5
|
+
require 'arjdbc/jdbc/serialized_attributes_helper'
|
5
6
|
|
6
|
-
module
|
7
|
+
module ArJdbc
|
7
8
|
module MsSQL
|
8
9
|
include TSqlMethods
|
9
10
|
include LimitHelpers
|
10
11
|
|
12
|
+
@@_lob_callback_added = nil
|
13
|
+
|
11
14
|
def self.extended(mod)
|
12
|
-
unless
|
15
|
+
unless @@_lob_callback_added
|
13
16
|
ActiveRecord::Base.class_eval do
|
14
17
|
def after_save_with_mssql_lob
|
15
|
-
self.class.columns.select { |c| c.sql_type =~ /image/i }.each do |
|
16
|
-
value = self
|
17
|
-
if
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
next if value.nil? || (value == '')
|
25
|
-
|
26
|
-
connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
|
18
|
+
self.class.columns.select { |c| c.sql_type =~ /image/i }.each do |column|
|
19
|
+
value = ::ArJdbc::SerializedAttributesHelper.dump_column_value(self, column)
|
20
|
+
next if value.nil? || (value == '')
|
21
|
+
|
22
|
+
connection.write_large_object(
|
23
|
+
column.type == :binary, column.name,
|
24
|
+
self.class.table_name, self.class.primary_key,
|
25
|
+
quote_value(id), value
|
26
|
+
)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
ActiveRecord::Base.after_save :after_save_with_mssql_lob
|
32
|
-
|
32
|
+
@@_lob_callback_added = true
|
33
33
|
end
|
34
34
|
mod.add_version_specific_add_limit_offset
|
35
35
|
end
|
@@ -217,9 +217,9 @@ module ::ArJdbc
|
|
217
217
|
else
|
218
218
|
super
|
219
219
|
end
|
220
|
-
when TrueClass
|
221
|
-
when FalseClass
|
222
|
-
else
|
220
|
+
when TrueClass then '1'
|
221
|
+
when FalseClass then '0'
|
222
|
+
else super
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
@@ -319,12 +319,14 @@ module ::ArJdbc
|
|
319
319
|
execute "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
|
320
320
|
end
|
321
321
|
end
|
322
|
-
|
323
|
-
def remove_column(table_name,
|
322
|
+
|
323
|
+
def remove_column(table_name, *column_names) #:nodoc:
|
324
324
|
clear_cached_table(table_name)
|
325
|
-
|
326
|
-
|
327
|
-
|
325
|
+
for column_name in column_names.flatten
|
326
|
+
remove_check_constraints(table_name, column_name)
|
327
|
+
remove_default_constraint(table_name, column_name)
|
328
|
+
execute "ALTER TABLE #{table_name} DROP COLUMN [#{column_name}]"
|
329
|
+
end
|
328
330
|
end
|
329
331
|
|
330
332
|
def remove_default_constraint(table_name, column_name)
|
@@ -369,36 +371,6 @@ module ::ArJdbc
|
|
369
371
|
@table_columns[table_name]
|
370
372
|
end
|
371
373
|
|
372
|
-
def _execute(sql, name = nil)
|
373
|
-
# Match the start of the sql to determine appropriate behaviour. Be aware of
|
374
|
-
# multi-line sql which might begin with 'create stored_proc' and contain 'insert into ...' lines.
|
375
|
-
# Possible improvements include ignoring comment blocks prior to the first statement.
|
376
|
-
if sql.lstrip =~ /\Ainsert/i
|
377
|
-
if query_requires_identity_insert?(sql)
|
378
|
-
table_name = get_table_name(sql)
|
379
|
-
with_identity_insert_enabled(table_name) do
|
380
|
-
id = @connection.execute_insert(sql)
|
381
|
-
end
|
382
|
-
else
|
383
|
-
@connection.execute_insert(sql)
|
384
|
-
end
|
385
|
-
elsif sql.lstrip =~ /\A(create|exec)/i
|
386
|
-
@connection.execute_update(sql)
|
387
|
-
elsif sql.lstrip =~ /\A\(?\s*(select|show)/i
|
388
|
-
repair_special_columns(sql)
|
389
|
-
@connection.execute_query(sql)
|
390
|
-
else
|
391
|
-
@connection.execute_update(sql)
|
392
|
-
end
|
393
|
-
end
|
394
|
-
|
395
|
-
def select(sql, name = nil, binds = [])
|
396
|
-
sql = substitute_binds(sql, binds)
|
397
|
-
log(sql, name) do
|
398
|
-
@connection.execute_query(sql)
|
399
|
-
end
|
400
|
-
end
|
401
|
-
|
402
374
|
# Turns IDENTITY_INSERT ON for table during execution of the block
|
403
375
|
# N.B. This sets the state of IDENTITY_INSERT to OFF after the
|
404
376
|
# block has been executed without regard to its previous state
|
@@ -478,6 +450,32 @@ module ::ArJdbc
|
|
478
450
|
def reset_column_information
|
479
451
|
@table_columns = nil
|
480
452
|
end
|
453
|
+
|
454
|
+
private
|
455
|
+
|
456
|
+
def _execute(sql, name = nil)
|
457
|
+
# Match the start of the SQL to determine appropriate behavior.
|
458
|
+
# Be aware of multi-line SQL which might begin with 'create stored_proc'
|
459
|
+
# and contain 'insert into ...' lines.
|
460
|
+
# TODO test and refactor using `self.class.insert?(sql)` etc
|
461
|
+
# NOTE: ignoring comment blocks prior to the first statement ?!
|
462
|
+
if sql.lstrip =~ /\Ainsert/i # self.class.insert?(sql)
|
463
|
+
if query_requires_identity_insert?(sql)
|
464
|
+
table_name = get_table_name(sql)
|
465
|
+
with_identity_insert_enabled(table_name) do
|
466
|
+
@connection.execute_insert(sql)
|
467
|
+
end
|
468
|
+
else
|
469
|
+
@connection.execute_insert(sql)
|
470
|
+
end
|
471
|
+
elsif sql.lstrip =~ /\A\(?\s*(select|show)/i # self.class.select?(sql)
|
472
|
+
repair_special_columns(sql)
|
473
|
+
@connection.execute_query(sql)
|
474
|
+
else # sql.lstrip =~ /\A(create|exec)/i
|
475
|
+
@connection.execute_update(sql)
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
481
479
|
end
|
482
480
|
end
|
483
481
|
|
@@ -1,7 +1,14 @@
|
|
1
1
|
class ActiveRecord::Base
|
2
2
|
class << self
|
3
3
|
def mssql_connection(config)
|
4
|
-
|
4
|
+
begin
|
5
|
+
require 'jdbc/jtds'
|
6
|
+
# NOTE: the adapter has only support for working with the
|
7
|
+
# open-source jTDS driver (won't work with MS's driver) !
|
8
|
+
::Jdbc::JTDS.load_driver(:require) if defined?(::Jdbc::JTDS.load_driver)
|
9
|
+
rescue LoadError => e # assuming driver.jar is on the class-path
|
10
|
+
raise e unless e.message.to_s.index('no such file to load')
|
11
|
+
end
|
5
12
|
|
6
13
|
config[:host] ||= "localhost"
|
7
14
|
config[:port] ||= 1433
|
data/lib/arjdbc/mysql.rb
CHANGED
data/lib/arjdbc/mysql/adapter.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'bigdecimal'
|
2
2
|
require 'active_record/connection_adapters/abstract/schema_definitions'
|
3
|
+
require 'arjdbc/mysql/explain_support'
|
3
4
|
|
4
|
-
module
|
5
|
+
module ArJdbc
|
5
6
|
module MySQL
|
6
|
-
def self.column_selector
|
7
|
-
[/mysql/i, lambda {|cfg,col| col.extend(::ArJdbc::MySQL::ColumnExtensions)}]
|
8
|
-
end
|
9
7
|
|
10
8
|
def self.extended(adapter)
|
11
9
|
adapter.configure_connection
|
@@ -15,11 +13,16 @@ module ::ArJdbc
|
|
15
13
|
execute("SET SQL_AUTO_IS_NULL=0")
|
16
14
|
end
|
17
15
|
|
16
|
+
def self.column_selector
|
17
|
+
[ /mysql/i, lambda { |_,column| column.extend(::ArJdbc::MySQL::Column) } ]
|
18
|
+
end
|
19
|
+
|
18
20
|
def self.jdbc_connection_class
|
19
21
|
::ActiveRecord::ConnectionAdapters::MySQLJdbcConnection
|
20
22
|
end
|
21
23
|
|
22
|
-
module
|
24
|
+
module Column
|
25
|
+
|
23
26
|
def extract_default(default)
|
24
27
|
if sql_type =~ /blob/i || type == :text
|
25
28
|
if default.blank?
|
@@ -60,14 +63,15 @@ module ::ArJdbc
|
|
60
63
|
when /long/i
|
61
64
|
2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
|
62
65
|
else
|
63
|
-
|
66
|
+
super # we could return 65535 here, but we leave it undecorated by default
|
64
67
|
end
|
65
|
-
when /^enum/i; 255
|
66
68
|
when /^bigint/i; 8
|
67
69
|
when /^int/i; 4
|
68
70
|
when /^mediumint/i; 3
|
69
71
|
when /^smallint/i; 2
|
70
72
|
when /^tinyint/i; 1
|
73
|
+
when /^enum\((.+)\)/i # 255
|
74
|
+
$1.split(',').map{ |enum| enum.strip.length - 2 }.max
|
71
75
|
when /^(bool|date|float|int|time)/i
|
72
76
|
nil
|
73
77
|
else
|
@@ -85,23 +89,51 @@ module ::ArJdbc
|
|
85
89
|
def missing_default_forged_as_empty_string?(default)
|
86
90
|
type != :string && !null && default == ''
|
87
91
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
ColumnExtensions = Column # :nodoc: backwards-compatibility
|
96
|
+
|
97
|
+
NATIVE_DATABASE_TYPES = {
|
98
|
+
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
|
99
|
+
:string => { :name => "varchar", :limit => 255 },
|
100
|
+
:text => { :name => "text" },
|
101
|
+
:integer => { :name => "int", :limit => 4 },
|
102
|
+
:float => { :name => "float" },
|
103
|
+
:decimal => { :name => "decimal" },
|
104
|
+
:datetime => { :name => "datetime" },
|
105
|
+
:timestamp => { :name => "datetime" },
|
106
|
+
:time => { :name => "time" },
|
107
|
+
:date => { :name => "date" },
|
108
|
+
:binary => { :name => "blob" },
|
109
|
+
:boolean => { :name => "tinyint", :limit => 1 }
|
110
|
+
}
|
111
|
+
|
112
|
+
def native_database_types
|
113
|
+
NATIVE_DATABASE_TYPES
|
114
|
+
end
|
115
|
+
|
116
|
+
def modify_types(types)
|
117
|
+
types[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
|
118
|
+
types[:integer] = { :name => 'int', :limit => 4 }
|
119
|
+
types[:decimal] = { :name => "decimal" }
|
120
|
+
types[:timestamp] = { :name => "datetime" }
|
121
|
+
types[:datetime][:limit] = nil
|
122
|
+
types
|
123
|
+
end
|
124
|
+
|
125
|
+
ADAPTER_NAME = 'MySQL'.freeze
|
126
|
+
|
99
127
|
def adapter_name #:nodoc:
|
100
|
-
|
128
|
+
ADAPTER_NAME
|
101
129
|
end
|
102
130
|
|
103
131
|
def self.arel2_visitors(config)
|
104
|
-
{
|
132
|
+
{
|
133
|
+
'mysql' => ::Arel::Visitors::MySQL,
|
134
|
+
'mysql2' => ::Arel::Visitors::MySQL,
|
135
|
+
'jdbcmysql' => ::Arel::Visitors::MySQL
|
136
|
+
}
|
105
137
|
end
|
106
138
|
|
107
139
|
def case_sensitive_equality_operator
|
@@ -120,32 +152,57 @@ module ::ArJdbc
|
|
120
152
|
|
121
153
|
def quote(value, column = nil)
|
122
154
|
return value.quoted_id if value.respond_to?(:quoted_id)
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
"
|
129
|
-
elsif BigDecimal === value
|
130
|
-
"'#{value.to_s("F")}'"
|
155
|
+
return value.to_s if column && column.type == :primary_key
|
156
|
+
|
157
|
+
if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
158
|
+
"x'#{column.class.string_to_binary(value).unpack("H*")[0]}'"
|
159
|
+
elsif value.kind_of?(BigDecimal)
|
160
|
+
value.to_s("F")
|
131
161
|
else
|
132
162
|
super
|
133
163
|
end
|
134
164
|
end
|
135
|
-
|
136
|
-
def quote_column_name(name)
|
165
|
+
|
166
|
+
def quote_column_name(name) # :nodoc:
|
137
167
|
"`#{name.to_s.gsub('`', '``')}`"
|
138
168
|
end
|
169
|
+
|
170
|
+
def quote_table_name(name) # :nodoc:
|
171
|
+
quote_column_name(name).gsub('.', '`.`')
|
172
|
+
end
|
173
|
+
|
174
|
+
# Returns true, since this connection adapter supports migrations.
|
175
|
+
def supports_migrations?
|
176
|
+
true
|
177
|
+
end
|
139
178
|
|
140
|
-
def
|
141
|
-
|
179
|
+
def supports_primary_key? # :nodoc:
|
180
|
+
true
|
142
181
|
end
|
143
182
|
|
144
|
-
def
|
145
|
-
|
183
|
+
def supports_bulk_alter? # :nodoc:
|
184
|
+
true
|
146
185
|
end
|
147
186
|
|
148
|
-
|
187
|
+
# Technically MySQL allows to create indexes with the sort order syntax
|
188
|
+
# but at the moment (5.5) it doesn't yet implement them
|
189
|
+
def supports_index_sort_order? # :nodoc:
|
190
|
+
true
|
191
|
+
end
|
192
|
+
|
193
|
+
# MySQL 4 technically support transaction isolation, but it is affected by a bug
|
194
|
+
# where the transaction level gets persisted for the whole session:
|
195
|
+
#
|
196
|
+
# http://bugs.mysql.com/bug.php?id=39170
|
197
|
+
def supports_transaction_isolation? # :nodoc:
|
198
|
+
version[0] && version[0] >= 5
|
199
|
+
end
|
200
|
+
|
201
|
+
def supports_views? # :nodoc:
|
202
|
+
version[0] && version[0] >= 5
|
203
|
+
end
|
204
|
+
|
205
|
+
def supports_savepoints? # :nodoc:
|
149
206
|
true
|
150
207
|
end
|
151
208
|
|
@@ -161,18 +218,31 @@ module ::ArJdbc
|
|
161
218
|
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
162
219
|
end
|
163
220
|
|
164
|
-
def disable_referential_integrity
|
165
|
-
|
221
|
+
def disable_referential_integrity # :nodoc:
|
222
|
+
fk_checks = select_value("SELECT @@FOREIGN_KEY_CHECKS")
|
166
223
|
begin
|
167
224
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
168
225
|
yield
|
169
226
|
ensure
|
170
|
-
update("SET FOREIGN_KEY_CHECKS = #{
|
227
|
+
update("SET FOREIGN_KEY_CHECKS = #{fk_checks}")
|
171
228
|
end
|
172
229
|
end
|
173
230
|
|
231
|
+
# DATABASE STATEMENTS ======================================
|
232
|
+
|
233
|
+
def exec_insert(sql, name, binds)
|
234
|
+
execute sql, name, binds
|
235
|
+
end
|
236
|
+
alias :exec_update :exec_insert
|
237
|
+
alias :exec_delete :exec_insert
|
238
|
+
|
239
|
+
# Make it public just like native MySQL adapter does.
|
240
|
+
def update_sql(sql, name = nil) # :nodoc:
|
241
|
+
super
|
242
|
+
end
|
243
|
+
|
174
244
|
# SCHEMA STATEMENTS ========================================
|
175
|
-
|
245
|
+
|
176
246
|
def structure_dump #:nodoc:
|
177
247
|
if supports_views?
|
178
248
|
sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
|
@@ -270,6 +340,11 @@ module ::ArJdbc
|
|
270
340
|
execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
|
271
341
|
end
|
272
342
|
|
343
|
+
def remove_index!(table_name, index_name) #:nodoc:
|
344
|
+
# missing table_name quoting in AR-2.3
|
345
|
+
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
346
|
+
end
|
347
|
+
|
273
348
|
def add_column(table_name, column_name, type, options = {})
|
274
349
|
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])}"
|
275
350
|
add_column_options!(add_column_sql, options)
|
@@ -373,15 +448,33 @@ module ::ArJdbc
|
|
373
448
|
end
|
374
449
|
|
375
450
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
451
|
+
case type.to_s
|
452
|
+
when 'binary'
|
453
|
+
case limit
|
454
|
+
when 0..0xfff; "varbinary(#{limit})"
|
455
|
+
when nil; "blob"
|
456
|
+
when 0x1000..0xffffffff; "blob(#{limit})"
|
457
|
+
else raise(ActiveRecordError, "No binary type has character length #{limit}")
|
458
|
+
end
|
459
|
+
when 'integer'
|
460
|
+
case limit
|
461
|
+
when 1; 'tinyint'
|
462
|
+
when 2; 'smallint'
|
463
|
+
when 3; 'mediumint'
|
464
|
+
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
|
465
|
+
when 5..8; 'bigint'
|
466
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
467
|
+
end
|
468
|
+
when 'text'
|
469
|
+
case limit
|
470
|
+
when 0..0xff; 'tinytext'
|
471
|
+
when nil, 0x100..0xffff; 'text'
|
472
|
+
when 0x10000..0xffffff; 'mediumtext'
|
473
|
+
when 0x1000000..0xffffffff; 'longtext'
|
474
|
+
else raise(ActiveRecordError, "No text type has character length #{limit}")
|
475
|
+
end
|
476
|
+
else
|
477
|
+
super
|
385
478
|
end
|
386
479
|
end
|
387
480
|
|
@@ -421,6 +514,7 @@ module ::ArJdbc
|
|
421
514
|
end
|
422
515
|
|
423
516
|
private
|
517
|
+
|
424
518
|
def column_for(table_name, column_name)
|
425
519
|
unless column = columns(table_name).find { |c| c.name == column_name.to_s }
|
426
520
|
raise "No such column: #{table_name}.#{column_name}"
|
@@ -431,10 +525,22 @@ module ::ArJdbc
|
|
431
525
|
def show_create_table(table)
|
432
526
|
select_one("SHOW CREATE TABLE #{quote_table_name(table)}")
|
433
527
|
end
|
434
|
-
|
435
|
-
def
|
436
|
-
|
528
|
+
|
529
|
+
def version
|
530
|
+
return @version ||= begin
|
531
|
+
version = []
|
532
|
+
java_connection = jdbc_connection(true)
|
533
|
+
if java_connection.is_a?(Java::ComMysqlJdbc::ConnectionImpl)
|
534
|
+
version << jdbc_connection.serverMajorVersion
|
535
|
+
version << jdbc_connection.serverMinorVersion
|
536
|
+
version << jdbc_connection.serverSubMinorVersion
|
537
|
+
else
|
538
|
+
warn "INFO: failed to resolve MySQL server version using: #{java_connection}"
|
539
|
+
end
|
540
|
+
version
|
541
|
+
end
|
437
542
|
end
|
543
|
+
|
438
544
|
end
|
439
545
|
end
|
440
546
|
|
@@ -445,7 +551,7 @@ module ActiveRecord
|
|
445
551
|
remove_const(:MysqlAdapter) if const_defined?(:MysqlAdapter)
|
446
552
|
|
447
553
|
class MysqlColumn < JdbcColumn
|
448
|
-
include ArJdbc::MySQL::
|
554
|
+
include ::ArJdbc::MySQL::Column
|
449
555
|
|
450
556
|
def initialize(name, *args)
|
451
557
|
if Hash === name
|
@@ -460,128 +566,58 @@ module ActiveRecord
|
|
460
566
|
end
|
461
567
|
|
462
568
|
class MysqlAdapter < JdbcAdapter
|
463
|
-
include ArJdbc::MySQL
|
569
|
+
include ::ArJdbc::MySQL
|
570
|
+
include ::ArJdbc::MySQL::ExplainSupport
|
464
571
|
|
465
572
|
def initialize(*args)
|
466
573
|
super
|
467
574
|
configure_connection
|
468
575
|
end
|
469
576
|
|
470
|
-
## EXPLAIN support lifted from the mysql2 gem with slight modifications
|
471
|
-
## to work in the JDBC adapter gem.
|
472
|
-
def supports_explain?
|
473
|
-
true
|
474
|
-
end
|
475
|
-
|
476
|
-
def explain(arel, binds = [])
|
477
|
-
sql = "EXPLAIN #{to_sql(arel, binds.dup)}"
|
478
|
-
start = Time.now.to_f
|
479
|
-
raw_result = execute(sql, "EXPLAIN")
|
480
|
-
ar_result = ActiveRecord::Result.new(raw_result[0].keys, raw_result)
|
481
|
-
elapsed = Time.now.to_f - start
|
482
|
-
ExplainPrettyPrinter.new.pp(ar_result, elapsed)
|
483
|
-
end
|
484
|
-
|
485
|
-
class ExplainPrettyPrinter # :nodoc:
|
486
|
-
# Pretty prints the result of a EXPLAIN in a way that resembles the output of the
|
487
|
-
# MySQL shell:
|
488
|
-
#
|
489
|
-
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
490
|
-
# | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|
491
|
-
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
492
|
-
# | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
|
493
|
-
# | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
|
494
|
-
# +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|
495
|
-
# 2 rows in set (0.00 sec)
|
496
|
-
#
|
497
|
-
# This is an exercise in Ruby hyperrealism :).
|
498
|
-
def pp(result, elapsed)
|
499
|
-
widths = compute_column_widths(result)
|
500
|
-
separator = build_separator(widths)
|
501
|
-
|
502
|
-
pp = []
|
503
|
-
|
504
|
-
pp << separator
|
505
|
-
pp << build_cells(result.columns, widths)
|
506
|
-
pp << separator
|
507
|
-
|
508
|
-
result.rows.each do |row|
|
509
|
-
pp << build_cells(row.values, widths)
|
510
|
-
end
|
511
|
-
|
512
|
-
pp << separator
|
513
|
-
pp << build_footer(result.rows.length, elapsed)
|
514
|
-
|
515
|
-
pp.join("\n") + "\n"
|
516
|
-
end
|
517
|
-
|
518
|
-
private
|
519
|
-
|
520
|
-
def compute_column_widths(result)
|
521
|
-
[].tap do |widths|
|
522
|
-
result.columns.each do |col|
|
523
|
-
cells_in_column = [col] + result.rows.map {|r| r[col].nil? ? 'NULL' : r[col].to_s}
|
524
|
-
widths << cells_in_column.map(&:length).max
|
525
|
-
end
|
526
|
-
end
|
527
|
-
|
528
|
-
end
|
529
|
-
|
530
|
-
def build_separator(widths)
|
531
|
-
padding = 1
|
532
|
-
'+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
|
533
|
-
end
|
534
|
-
|
535
|
-
def build_cells(items, widths)
|
536
|
-
cells = []
|
537
|
-
items.each_with_index do |item, i|
|
538
|
-
item = 'NULL' if item.nil?
|
539
|
-
justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
|
540
|
-
cells << item.to_s.send(justifier, widths[i])
|
541
|
-
end
|
542
|
-
'| ' + cells.join(' | ') + ' |'
|
543
|
-
end
|
544
|
-
|
545
|
-
def build_footer(nrows, elapsed)
|
546
|
-
rows_label = nrows == 1 ? 'row' : 'rows'
|
547
|
-
"#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
|
548
|
-
end
|
549
|
-
end
|
550
|
-
|
551
577
|
def jdbc_connection_class(spec)
|
552
578
|
::ArJdbc::MySQL.jdbc_connection_class
|
553
579
|
end
|
554
580
|
|
555
581
|
def jdbc_column_class
|
556
|
-
|
582
|
+
MysqlColumn
|
557
583
|
end
|
558
|
-
|
559
584
|
alias_chained_method :columns, :query_cache, :jdbc_columns
|
560
585
|
|
561
|
-
|
562
|
-
def exec_insert(sql, name, binds)
|
563
|
-
binds = binds.dup
|
586
|
+
# some QUOTING caching :
|
564
587
|
|
565
|
-
|
566
|
-
|
567
|
-
|
588
|
+
@@quoted_table_names = {}
|
589
|
+
|
590
|
+
def quote_table_name(name)
|
591
|
+
unless quoted = @@quoted_table_names[name]
|
592
|
+
quoted = super
|
593
|
+
@@quoted_table_names[name] = quoted.freeze
|
568
594
|
end
|
569
|
-
|
595
|
+
quoted
|
570
596
|
end
|
571
|
-
alias :exec_update :exec_insert
|
572
|
-
alias :exec_delete :exec_insert
|
573
597
|
|
598
|
+
@@quoted_column_names = {}
|
599
|
+
|
600
|
+
def quote_column_name(name)
|
601
|
+
unless quoted = @@quoted_column_names[name]
|
602
|
+
quoted = super
|
603
|
+
@@quoted_column_names[name] = quoted.freeze
|
604
|
+
end
|
605
|
+
quoted
|
606
|
+
end
|
607
|
+
|
574
608
|
end
|
575
609
|
end
|
576
610
|
end
|
577
611
|
|
578
|
-
|
579
|
-
|
612
|
+
# Don't need to load native mysql adapter
|
613
|
+
$LOADED_FEATURES << 'active_record/connection_adapters/mysql_adapter.rb'
|
614
|
+
$LOADED_FEATURES << 'active_record/connection_adapters/mysql2_adapter.rb'
|
580
615
|
|
581
|
-
|
582
|
-
|
616
|
+
module Mysql # :nodoc:
|
617
|
+
remove_const(:Error) if const_defined?(:Error)
|
618
|
+
class Error < ::ActiveRecord::JDBCError; end
|
583
619
|
|
584
620
|
def self.client_version
|
585
|
-
50400
|
621
|
+
50400 # faked out for AR tests
|
586
622
|
end
|
587
623
|
end
|