Skip to content

Commit

Permalink
JRUBY-2422: Fix MySQL referential integrity and rollback issues
Browse files Browse the repository at this point in the history
git-svn-id: svn+ssh://rubyforge.org/var/svn/jruby-extras/trunk/activerecord-jdbc@1012 8ba958d5-0c1a-0410-94a6-a65dfc1b28a6
  • Loading branch information
nicksieger committed Jun 4, 2008
1 parent bbd7d83 commit cb1436f
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 37 deletions.
4 changes: 3 additions & 1 deletion History.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
- 20243: numerics improvements for MSSQL (Aslak Hellesøy)
- 20172: don't quote table names for MSSQL (Thor Marius Henrichsen)
- 19729: check for primary key existence in postgres during insert (Martin Luder)
- 18846: retrying failing SQL statements is harmful when not autocommitting (Craig McMillan)
- JRUBY-2297, 18846: retrying failing SQL statements is harmful when not autocommitting (Craig McMillan)
- 10021: very preliminary sybase support. (Mark Atkinson) Not usable until collision w/ sqlserver driver is resolved.
- JRUBY-2312, JRUBY-2319, JRUBY-2322: Oracle timestamping issues (Jesse Hu & Michael König)
- JRUBY-2422: Fix MySQL referential integrity and rollback issues

== 0.8

Expand Down
59 changes: 34 additions & 25 deletions lib/jdbc_adapter/jdbc_mysql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ def self.column_selector
def self.adapter_selector
[/mysql/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::MySQL)}]
end

def self.extended(adapter)
adapter.execute("SET SQL_AUTO_IS_NULL=0")
end

module Column
TYPES_ALLOWING_EMPTY_STRING_DEFAULT = Set.new([:binary, :string, :text])

def simplified_type(field_type)
return :boolean if field_type =~ /tinyint\(1\)|bit/i
return :boolean if field_type =~ /tinyint\(1\)|bit/i
return :string if field_type =~ /enum/i
super
end
Expand All @@ -44,7 +44,7 @@ def init_column(name, default, *args)
@original_default = default
@default = nil if missing_default_forged_as_empty_string?
end

# MySQL misreports NOT NULL column default when none is given.
# We can't detect this for columns which may have a legitimate ''
# default (string, text, binary) but we can for others (integer,
Expand All @@ -56,20 +56,20 @@ def missing_default_forged_as_empty_string?
!null && @original_default == '' && !TYPES_ALLOWING_EMPTY_STRING_DEFAULT.include?(type)
end
end

def modify_types(tp)
tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
tp[:decimal] = { :name => "decimal" }
tp[:timestamp] = { :name => "datetime" }
tp[:datetime][:limit] = nil
tp
end

# QUOTING ==================================================

def quote(value, column = nil)
return value.quoted_id if value.respond_to?(:quoted_id)

if column && column.type == :primary_key
value.to_s
elsif column && String === value && column.type == :binary && column.class.respond_to?(:string_to_binary)
Expand All @@ -81,23 +81,23 @@ def quote(value, column = nil)
super
end
end

def quote_column_name(name) #:nodoc:
"`#{name}`"
end

def quote_table_name(name) #:nodoc:
quote_column_name(name).gsub('.', '`.`')
end

def quoted_true
"1"
end

def quoted_false
"0"
end

def begin_db_transaction #:nodoc:
@connection.begin
rescue Exception
Expand All @@ -116,16 +116,25 @@ def rollback_db_transaction #:nodoc:
# Transactions aren't supported
end

def disable_referential_integrity(&block) #:nodoc:
old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
begin
update("SET FOREIGN_KEY_CHECKS = 0")
yield
ensure
update("SET FOREIGN_KEY_CHECKS = #{old}")
end
end

# SCHEMA STATEMENTS ========================================

def structure_dump #:nodoc:
if supports_views?
sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
else
sql = "SHOW TABLES"
end

select_all(sql).inject("") do |structure, table|
table.delete('Table_type')

Expand All @@ -135,45 +144,45 @@ def structure_dump #:nodoc:
structure += table + ";\n\n"
elsif(view = hash["Create View"])
structure += view + ";\n\n"
end
end
end
end

def recreate_database(name) #:nodoc:
drop_database(name)
create_database(name)
end

def create_database(name, options = {}) #:nodoc:
if options[:collation]
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
else
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
end
end

def drop_database(name) #:nodoc:
execute "DROP DATABASE IF EXISTS `#{name}`"
end

def current_database
select_one("SELECT DATABASE() as db")["db"]
end

def create_table(name, options = {}) #:nodoc:
super(name, {:options => "ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin"}.merge(options))
end

def rename_table(name, new_name)
execute "RENAME TABLE #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
end
end

def change_column_default(table_name, column_name, default) #:nodoc:
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]

execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}")
end

def change_column(table_name, column_name, type, options = {}) #:nodoc:
unless options_include_default?(options)
if column = columns(table_name).find { |c| c.name == column_name.to_s }
Expand All @@ -193,7 +202,7 @@ def rename_column(table_name, column_name, new_column_name) #:nodoc:
current_type = cols["Type"] || cols["COLUMN_TYPE"]
execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_table_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
end

def add_limit_offset!(sql, options) #:nodoc:
if limit = options[:limit]
unless offset = options[:offset]
Expand Down
28 changes: 17 additions & 11 deletions src/java/jdbc_adapter/JdbcAdapterInternalService.java
Original file line number Diff line number Diff line change
Expand Up @@ -397,22 +397,28 @@ public static IRubyObject begin(IRubyObject recv) throws SQLException {

@JRubyMethod(name = "commit")
public static IRubyObject commit(IRubyObject recv) throws SQLException {
try {
getConnection(recv).commit();
return recv.getRuntime().getNil();
} finally {
getConnection(recv).setAutoCommit(true);
Connection c = getConnection(recv);
if (!c.getAutoCommit()) {
try {
c.commit();
} finally {
c.setAutoCommit(true);
}
}
return recv.getRuntime().getNil();
}

@JRubyMethod(name = "rollback")
public static IRubyObject rollback(IRubyObject recv) throws SQLException {
try {
getConnection(recv).rollback();
return recv.getRuntime().getNil();
} finally {
getConnection(recv).setAutoCommit(true);
Connection c = getConnection(recv);
if (!c.getAutoCommit()) {
try {
c.rollback();
} finally {
c.setAutoCommit(true);
}
}
return recv.getRuntime().getNil();
}

@JRubyMethod(name = {"columns", "columns_internal"}, required = 1, optional = 2)
Expand Down Expand Up @@ -1081,7 +1087,7 @@ private static boolean isConnectionBroken(IRubyObject recv, Connection c) {
} finally {
try { s.close(); } catch (SQLException ignored) {}
}
return true;
return false;
} else {
return !c.isClosed();
}
Expand Down

0 comments on commit cb1436f

Please sign in to comment.