Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' into vertica

  • Loading branch information...
commit 298ad7ee68f20e8d26dbd6d1b3ab28be8a38cabd 2 parents 15a6e58 + 1dccff1
@camilo authored
View
6 CHANGELOG
@@ -1,5 +1,11 @@
=== HEAD
+* Correctly handle errors that occur when rolling back transactions (jeremyevans)
+
+* Recognize identity type in the schema dumper (jeremyevans) (#468)
+
+* Fix possible thread safety issues in creating Database objects and anonymous model classes (jeremyevans)
+
* Don't assign instance variables to Java objects, for future JRuby 2.0 support (jeremyevans) (#466)
* Use date and timestamp formats that are multilanguage and not DATEFORMAT dependent on Microsoft SQL Server (jeremyevans)
View
1  doc/prepared_statements.rdoc
@@ -82,7 +82,6 @@ and update queries, the hash to insert/update is passed to +prepare+:
If you are using the ruby-postgres or postgres-pr driver, PostgreSQL uses the
default emulated support. If you are using ruby-pg, there is native support,
but it may require type specifiers on some old versions (generally not anymore).
-direct control over the SQL string, but since Sequel abstracts that, the types
You can add a __* suffix to the placeholder symbol to specify a type, which
casts to that type in the SQL (e.g. :$name__text, which will be compiled to
"$1::text" in the SQL). Prepared statements are always server side.
View
9 lib/sequel/core.rb
@@ -34,6 +34,9 @@ module Sequel
# Mutex used to protect file loading/requireing
@require_mutex = Mutex.new
+ # Mutex used to protect global data structures
+ @data_mutex = Mutex.new
+
class << self
# Sequel converts two digit years in <tt>Date</tt>s and <tt>DateTime</tt>s by default,
# so 01/02/03 is interpreted at January 2nd, 2003, and 12/13/99 is interpreted
@@ -282,6 +285,12 @@ def self.string_to_time(string)
end
end
+ # Protects access to any mutable global data structure in Sequel.
+ # Uses a non-reentrant mutex, so calling code should be careful.
+ def self.synchronize(&block)
+ @data_mutex.synchronize(&block)
+ end
+
# Uses a transaction on all given databases with the given options. This:
#
# Sequel.transaction([DB1, DB2, DB3]){...}
View
2  lib/sequel/database/connecting.rb
@@ -81,7 +81,7 @@ def self.connect(conn_string, opts = {})
ensure
if block_given?
db.disconnect if db
- ::Sequel::DATABASES.delete(db)
+ Sequel.synchronize{::Sequel::DATABASES.delete(db)}
end
end
block_given? ? result : db
View
2  lib/sequel/database/misc.rb
@@ -63,7 +63,7 @@ def initialize(opts = {}, &block)
self.sql_log_level = @opts[:sql_log_level] ? @opts[:sql_log_level].to_sym : :info
@pool = ConnectionPool.get_pool(@opts, &block)
- ::Sequel::DATABASES.push(self)
+ Sequel.synchronize{::Sequel::DATABASES.push(self)}
end
# If a transaction is not currently in process, yield to the block immediately.
View
6 lib/sequel/database/query.rb
@@ -286,7 +286,11 @@ def _transaction(conn, opts={})
yield(conn)
end
rescue Exception => e
- rollback_transaction(conn, opts)
+ begin
+ rollback_transaction(conn, opts)
+ rescue Exception => e3
+ raise_error(e3, :classes=>database_error_classes, :conn=>conn)
+ end
transaction_error(e, :conn=>conn, :rollback=>rollback)
ensure
begin
View
2  lib/sequel/extensions/schema_dumper.rb
@@ -175,7 +175,7 @@ def column_schema_to_ruby_type(schema)
{:type=>BigDecimal, :size=>(s.empty? ? nil : s)}
when /\A(?:bytea|(?:tiny|medium|long)?blob|(?:var)?binary)(?:\((\d+)\))?\z/o
{:type=>File, :size=>($1.to_i if $1)}
- when 'year'
+ when /\A(?:year|(?:int )?identity)\z/o
{:type=>Integer}
else
{:type=>String}
View
4 lib/sequel/model.rb
@@ -35,7 +35,7 @@ module Sequel
# dataset # => DB1[:comments]
# end
def self.Model(source)
- if Sequel::Model.cache_anonymous_models && (klass = Model::ANONYMOUS_MODEL_CLASSES[source])
+ if Sequel::Model.cache_anonymous_models && (klass = Sequel.synchronize{Model::ANONYMOUS_MODEL_CLASSES[source]})
return klass
end
klass = if source.is_a?(Database)
@@ -45,7 +45,7 @@ def self.Model(source)
else
Class.new(Model).set_dataset(source)
end
- Model::ANONYMOUS_MODEL_CLASSES[source] = klass if Sequel::Model.cache_anonymous_models
+ Sequel.synchronize{Model::ANONYMOUS_MODEL_CLASSES[source] = klass} if Sequel::Model.cache_anonymous_models
klass
end
View
2  lib/sequel/model/base.rb
@@ -220,7 +220,7 @@ def dataset_module(mod = nil)
# end # COMMIT
def db
return @db if @db
- @db = self == Model ? DATABASES.first : superclass.db
+ @db = self == Model ? Sequel.synchronize{DATABASES.first} : superclass.db
raise(Error, "No database associated with #{self}: have you called Sequel.connect or #{self}.db= ?") unless @db
@db
end
View
2  lib/sequel/plugins/timestamps.rb
@@ -19,7 +19,7 @@ module Plugins
# # timestamp, and setting the update timestamp when creating
# Album.plugin :timestamps, :force=>true, :update_on_create=>true
module Timestamps
- # Configure the plugin by setting the avialable options. Note that
+ # Configure the plugin by setting the available options. Note that
# if this method is run more than once, previous settings are ignored,
# and it will just use the settings given or the default settings. Options:
# * :create - The field to hold the create timestamp (default: :created_at)
View
39 spec/core/database_spec.rb
@@ -636,6 +636,45 @@ def @db.ret_commit
proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
end
+ specify "should handle errors when sending BEGIN" do
+ ec = Class.new(StandardError)
+ @db.meta_def(:database_error_classes){[ec]}
+ @db.meta_def(:log_connection_execute){|c, sql| sql =~ /BEGIN/ ? raise(ec, 'bad') : super(c, sql)}
+ begin
+ @db.transaction{@db.execute 'DROP TABLE test;'}
+ rescue Sequel::DatabaseError => e
+ end
+ e.should_not be_nil
+ e.wrapped_exception.should be_a_kind_of(ec)
+ @db.sqls.should == ['ROLLBACK']
+ end
+
+ specify "should handle errors when sending COMMIT" do
+ ec = Class.new(StandardError)
+ @db.meta_def(:database_error_classes){[ec]}
+ @db.meta_def(:log_connection_execute){|c, sql| sql =~ /COMMIT/ ? raise(ec, 'bad') : super(c, sql)}
+ begin
+ @db.transaction{@db.execute 'DROP TABLE test;'}
+ rescue Sequel::DatabaseError => e
+ end
+ e.should_not be_nil
+ e.wrapped_exception.should be_a_kind_of(ec)
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;']
+ end
+
+ specify "should handle errors when sending ROLLBACK" do
+ ec = Class.new(StandardError)
+ @db.meta_def(:database_error_classes){[ec]}
+ @db.meta_def(:log_connection_execute){|c, sql| sql =~ /ROLLBACK/ ? raise(ec, 'bad') : super(c, sql)}
+ begin
+ @db.transaction{raise ArgumentError, 'asdf'}
+ rescue Sequel::DatabaseError => e
+ end
+ e.should_not be_nil
+ e.wrapped_exception.should be_a_kind_of(ec)
+ @db.sqls.should == ['BEGIN']
+ end
+
specify "should issue ROLLBACK if Sequel::Rollback is called in the transaction" do
@db.transaction do
@db.drop_table(:a)
View
4 spec/extensions/schema_dumper_spec.rb
@@ -508,7 +508,7 @@
["double precision", "timestamp with time zone", "timestamp without time zone",
"time with time zone", "time without time zone", "character varying(20)"] +
%w"nvarchar ntext smalldatetime smallmoney binary varbinary nchar" +
- ["timestamp(6) without time zone", "timestamp(6) with time zone", "int(12) unsigned", 'bigint unsigned', 'tinyint(3) unsigned']
+ ["timestamp(6) without time zone", "timestamp(6) with time zone", "int(12) unsigned", 'bigint unsigned', 'tinyint(3) unsigned', 'identity', 'int identity']
@d.meta_def(:schema) do |t, *o|
i = 0
types.map{|x| [:"c#{i+=1}", {:db_type=>x, :allow_null=>true}]}
@@ -581,6 +581,8 @@
Integer :c64
Bignum :c65
Integer :c66
+ Integer :c67
+ Integer :c68
end
END_MIG
end
View
2  spec/integration/associations_test.rb
@@ -608,7 +608,7 @@ class ::Tag < Sequel::Model(@db)
@els = {:eager_limit_strategy=>:correlated_subquery}
end
it_should_behave_like "eager limit strategies"
- end unless Sequel.guarded?(:mysql, :db2, :oracle)
+ end unless Sequel.guarded?(:mysql, :db2, :oracle, :h2)
specify "should handle many_to_one associations with same name as :key" do
Album.def_column_alias(:artist_id_id, :artist_id)
Please sign in to comment.
Something went wrong with that request. Please try again.