activerecord 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +4928 -3
- data/README +45 -46
- data/RUNNING_UNIT_TESTS +8 -11
- data/Rakefile +247 -0
- data/install.rb +8 -38
- data/lib/active_record/aggregations.rb +64 -49
- data/lib/active_record/associations/association_collection.rb +217 -47
- data/lib/active_record/associations/association_proxy.rb +159 -0
- data/lib/active_record/associations/belongs_to_association.rb +56 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +155 -37
- data/lib/active_record/associations/has_many_association.rb +145 -75
- data/lib/active_record/associations/has_many_through_association.rb +283 -0
- data/lib/active_record/associations/has_one_association.rb +96 -0
- data/lib/active_record/associations.rb +1537 -304
- data/lib/active_record/attribute_methods.rb +328 -0
- data/lib/active_record/base.rb +2001 -588
- data/lib/active_record/calculations.rb +269 -0
- data/lib/active_record/callbacks.rb +169 -165
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +308 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +472 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +306 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +125 -279
- data/lib/active_record/connection_adapters/mysql_adapter.rb +442 -77
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +805 -135
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +353 -69
- data/lib/active_record/fixtures.rb +946 -100
- data/lib/active_record/locking/optimistic.rb +144 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +417 -0
- data/lib/active_record/observer.rb +142 -32
- data/lib/active_record/query_cache.rb +23 -0
- data/lib/active_record/reflection.rb +163 -70
- data/lib/active_record/schema.rb +58 -0
- data/lib/active_record/schema_dumper.rb +171 -0
- data/lib/active_record/serialization.rb +98 -0
- data/lib/active_record/serializers/json_serializer.rb +71 -0
- data/lib/active_record/serializers/xml_serializer.rb +315 -0
- data/lib/active_record/timestamp.rb +41 -0
- data/lib/active_record/transactions.rb +87 -57
- data/lib/active_record/validations.rb +909 -122
- data/lib/active_record/vendor/db2.rb +362 -0
- data/lib/active_record/vendor/mysql.rb +126 -29
- data/lib/active_record/version.rb +9 -0
- data/lib/active_record.rb +35 -7
- data/lib/activerecord.rb +1 -0
- data/test/aaa_create_tables_test.rb +72 -0
- data/test/abstract_unit.rb +73 -5
- data/test/active_schema_test_mysql.rb +43 -0
- data/test/adapter_test.rb +105 -0
- data/test/adapter_test_sqlserver.rb +95 -0
- data/test/aggregations_test.rb +110 -16
- data/test/all.sh +2 -2
- data/test/ar_schema_test.rb +33 -0
- data/test/association_inheritance_reload.rb +14 -0
- data/test/associations/ar_joins_test.rb +0 -0
- data/test/associations/callbacks_test.rb +147 -0
- data/test/associations/cascaded_eager_loading_test.rb +110 -0
- data/test/associations/eager_singularization_test.rb +145 -0
- data/test/associations/eager_test.rb +442 -0
- data/test/associations/extension_test.rb +47 -0
- data/test/associations/inner_join_association_test.rb +88 -0
- data/test/associations/join_model_test.rb +553 -0
- data/test/associations_test.rb +1930 -267
- data/test/attribute_methods_test.rb +146 -0
- data/test/base_test.rb +1316 -84
- data/test/binary_test.rb +32 -0
- data/test/calculations_test.rb +251 -0
- data/test/callbacks_test.rb +400 -0
- data/test/class_inheritable_attributes_test.rb +3 -4
- data/test/column_alias_test.rb +17 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connection_test_mysql.rb +30 -0
- data/test/connections/native_db2/connection.rb +25 -0
- data/test/connections/native_firebird/connection.rb +26 -0
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +21 -18
- data/test/connections/native_openbase/connection.rb +21 -0
- data/test/connections/native_oracle/connection.rb +27 -0
- data/test/connections/native_postgresql/connection.rb +17 -18
- data/test/connections/native_sqlite/connection.rb +17 -16
- data/test/connections/native_sqlite3/connection.rb +25 -0
- data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
- data/test/connections/native_sybase/connection.rb +23 -0
- data/test/copy_table_test_sqlite.rb +69 -0
- data/test/datatype_test_postgresql.rb +203 -0
- data/test/date_time_test.rb +37 -0
- data/test/default_test_firebird.rb +16 -0
- data/test/defaults_test.rb +67 -0
- data/test/deprecated_finder_test.rb +30 -0
- data/test/finder_test.rb +607 -32
- data/test/fixtures/accounts.yml +28 -0
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author.rb +107 -0
- data/test/fixtures/author_favorites.yml +4 -0
- data/test/fixtures/authors.yml +7 -0
- data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
- data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
- data/test/fixtures/bad_fixtures/blank_line +3 -0
- data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
- data/test/fixtures/bad_fixtures/missing_value +1 -0
- data/test/fixtures/binaries.yml +132 -0
- data/test/fixtures/binary.rb +2 -0
- data/test/fixtures/book.rb +4 -0
- data/test/fixtures/books.yml +7 -0
- data/test/fixtures/categories/special_categories.yml +9 -0
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
- data/test/fixtures/categories.yml +14 -0
- data/test/fixtures/categories_ordered.yml +7 -0
- data/test/fixtures/categories_posts.yml +23 -0
- data/test/fixtures/categorization.rb +5 -0
- data/test/fixtures/categorizations.yml +17 -0
- data/test/fixtures/category.rb +26 -0
- data/test/fixtures/citation.rb +6 -0
- data/test/fixtures/comment.rb +23 -0
- data/test/fixtures/comments.yml +59 -0
- data/test/fixtures/companies.yml +55 -0
- data/test/fixtures/company.rb +81 -4
- data/test/fixtures/company_in_module.rb +32 -6
- data/test/fixtures/computer.rb +4 -0
- data/test/fixtures/computers.yml +4 -0
- data/test/fixtures/contact.rb +16 -0
- data/test/fixtures/courses.yml +7 -0
- data/test/fixtures/customer.rb +28 -3
- data/test/fixtures/customers.yml +17 -0
- data/test/fixtures/db_definitions/db2.drop.sql +33 -0
- data/test/fixtures/db_definitions/db2.sql +235 -0
- data/test/fixtures/db_definitions/db22.drop.sql +2 -0
- data/test/fixtures/db_definitions/db22.sql +5 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +65 -0
- data/test/fixtures/db_definitions/firebird.sql +310 -0
- data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
- data/test/fixtures/db_definitions/firebird2.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +33 -0
- data/test/fixtures/db_definitions/frontbase.sql +273 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase.sql +318 -0
- data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase2.sql +7 -0
- data/test/fixtures/db_definitions/oracle.drop.sql +67 -0
- data/test/fixtures/db_definitions/oracle.sql +330 -0
- data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle2.sql +6 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +44 -0
- data/test/fixtures/db_definitions/postgresql.sql +217 -38
- data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/postgresql2.sql +2 -2
- data/test/fixtures/db_definitions/schema.rb +354 -0
- data/test/fixtures/db_definitions/schema2.rb +11 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +33 -0
- data/test/fixtures/db_definitions/sqlite.sql +139 -5
- data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlite2.sql +1 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
- data/test/fixtures/db_definitions/sybase.sql +222 -0
- data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
- data/test/fixtures/db_definitions/sybase2.sql +5 -0
- data/test/fixtures/developer.rb +70 -6
- data/test/fixtures/developers.yml +21 -0
- data/test/fixtures/developers_projects/david_action_controller +2 -1
- data/test/fixtures/developers_projects/david_active_record +2 -1
- data/test/fixtures/developers_projects.yml +17 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/entrants.yml +14 -0
- data/test/fixtures/example.log +1 -0
- data/test/fixtures/fk_test_has_fk.yml +3 -0
- data/test/fixtures/fk_test_has_pk.yml +2 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/funny_jokes.yml +10 -0
- data/test/fixtures/item.rb +7 -0
- data/test/fixtures/items.yml +4 -0
- data/test/fixtures/joke.rb +3 -0
- data/test/fixtures/keyboard.rb +3 -0
- data/test/fixtures/legacy_thing.rb +3 -0
- data/test/fixtures/legacy_things.yml +3 -0
- data/test/fixtures/matey.rb +4 -0
- data/test/fixtures/mateys.yml +4 -0
- data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
- data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/minimalistic.rb +2 -0
- data/test/fixtures/minimalistics.yml +2 -0
- data/test/fixtures/mixed_case_monkey.rb +3 -0
- data/test/fixtures/mixed_case_monkeys.yml +6 -0
- data/test/fixtures/mixins.yml +29 -0
- data/test/fixtures/movies.yml +7 -0
- data/test/fixtures/naked/csv/accounts.csv +1 -0
- data/test/fixtures/naked/yml/accounts.yml +1 -0
- data/test/fixtures/naked/yml/companies.yml +1 -0
- data/test/fixtures/naked/yml/courses.yml +1 -0
- data/test/fixtures/order.rb +4 -0
- data/test/fixtures/parrot.rb +13 -0
- data/test/fixtures/parrots.yml +27 -0
- data/test/fixtures/parrots_pirates.yml +7 -0
- data/test/fixtures/people.yml +3 -0
- data/test/fixtures/person.rb +4 -0
- data/test/fixtures/pirate.rb +5 -0
- data/test/fixtures/pirates.yml +9 -0
- data/test/fixtures/post.rb +59 -0
- data/test/fixtures/posts.yml +48 -0
- data/test/fixtures/project.rb +27 -2
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/reader.rb +4 -0
- data/test/fixtures/readers.yml +4 -0
- data/test/fixtures/reply.rb +18 -2
- data/test/fixtures/reserved_words/distinct.yml +5 -0
- data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
- data/test/fixtures/reserved_words/group.yml +14 -0
- data/test/fixtures/reserved_words/select.yml +8 -0
- data/test/fixtures/reserved_words/values.yml +7 -0
- data/test/fixtures/ship.rb +3 -0
- data/test/fixtures/ships.yml +5 -0
- data/test/fixtures/subject.rb +4 -0
- data/test/fixtures/subscriber.rb +4 -3
- data/test/fixtures/tag.rb +7 -0
- data/test/fixtures/tagging.rb +10 -0
- data/test/fixtures/taggings.yml +25 -0
- data/test/fixtures/tags.yml +7 -0
- data/test/fixtures/task.rb +3 -0
- data/test/fixtures/tasks.yml +7 -0
- data/test/fixtures/topic.rb +20 -3
- data/test/fixtures/topics.yml +22 -0
- data/test/fixtures/treasure.rb +4 -0
- data/test/fixtures/treasures.yml +10 -0
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +574 -8
- data/test/inheritance_test.rb +113 -27
- data/test/json_serialization_test.rb +180 -0
- data/test/lifecycle_test.rb +56 -29
- data/test/locking_test.rb +273 -0
- data/test/method_scoping_test.rb +416 -0
- data/test/migration_test.rb +933 -0
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_test.rb +95 -0
- data/test/modules_test.rb +23 -10
- data/test/multiple_db_test.rb +17 -3
- data/test/pk_test.rb +59 -15
- data/test/query_cache_test.rb +104 -0
- data/test/readonly_test.rb +107 -0
- data/test/reflection_test.rb +124 -27
- data/test/reserved_word_test_mysql.rb +177 -0
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +131 -0
- data/test/schema_test_postgresql.rb +64 -0
- data/test/serialization_test.rb +47 -0
- data/test/synonym_test_oracle.rb +17 -0
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +48 -0
- data/test/transactions_test.rb +227 -29
- data/test/unconnected_test.rb +14 -6
- data/test/validations_test.rb +1293 -32
- data/test/xml_serialization_test.rb +202 -0
- metadata +347 -143
- data/dev-utils/eval_debugger.rb +0 -9
- data/examples/associations.rb +0 -87
- data/examples/shared_setup.rb +0 -15
- data/examples/validation.rb +0 -88
- data/lib/active_record/deprecated_associations.rb +0 -70
- data/lib/active_record/support/class_attribute_accessors.rb +0 -43
- data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
- data/lib/active_record/support/clean_logger.rb +0 -10
- data/lib/active_record/support/inflector.rb +0 -70
- data/lib/active_record/vendor/simple.rb +0 -702
- data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
- data/lib/active_record/wrappings.rb +0 -59
- data/rakefile +0 -122
- data/test/deprecated_associations_test.rb +0 -336
- data/test/fixtures/accounts/signals37 +0 -3
- data/test/fixtures/accounts/unknown +0 -2
- data/test/fixtures/companies/first_client +0 -6
- data/test/fixtures/companies/first_firm +0 -4
- data/test/fixtures/companies/second_client +0 -6
- data/test/fixtures/courses/java +0 -2
- data/test/fixtures/courses/ruby +0 -2
- data/test/fixtures/customers/david +0 -6
- data/test/fixtures/db_definitions/mysql.sql +0 -96
- data/test/fixtures/db_definitions/mysql2.sql +0 -4
- data/test/fixtures/developers/david +0 -2
- data/test/fixtures/developers/jamis +0 -2
- data/test/fixtures/entrants/first +0 -3
- data/test/fixtures/entrants/second +0 -3
- data/test/fixtures/entrants/third +0 -3
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/movies/first +0 -2
- data/test/fixtures/movies/second +0 -2
- data/test/fixtures/projects/action_controller +0 -2
- data/test/fixtures/projects/active_record +0 -2
- data/test/fixtures/topics/first +0 -9
- data/test/fixtures/topics/second +0 -8
- data/test/inflector_test.rb +0 -104
- data/test/thread_safety_test.rb +0 -33
@@ -1,25 +1,79 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
-
require '
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module MysqlCompat #:nodoc:
|
5
|
+
# add all_hashes method to standard mysql-c bindings or pure ruby version
|
6
|
+
def self.define_all_hashes_method!
|
7
|
+
raise 'Mysql not loaded' unless defined?(::Mysql)
|
8
|
+
|
9
|
+
target = defined?(Mysql::Result) ? Mysql::Result : MysqlRes
|
10
|
+
return if target.instance_methods.include?('all_hashes')
|
11
|
+
|
12
|
+
# Ruby driver has a version string and returns null values in each_hash
|
13
|
+
# C driver >= 2.7 returns null values in each_hash
|
14
|
+
if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700)
|
15
|
+
target.class_eval <<-'end_eval'
|
16
|
+
def all_hashes
|
17
|
+
rows = []
|
18
|
+
each_hash { |row| rows << row }
|
19
|
+
rows
|
20
|
+
end
|
21
|
+
end_eval
|
22
|
+
|
23
|
+
# adapters before 2.7 don't have a version constant
|
24
|
+
# and don't return null values in each_hash
|
25
|
+
else
|
26
|
+
target.class_eval <<-'end_eval'
|
27
|
+
def all_hashes
|
28
|
+
rows = []
|
29
|
+
all_fields = fetch_fields.inject({}) { |fields, f| fields[f.name] = nil; fields }
|
30
|
+
each_hash { |row| rows << all_fields.dup.update(row) }
|
31
|
+
rows
|
32
|
+
end
|
33
|
+
end_eval
|
34
|
+
end
|
35
|
+
|
36
|
+
unless target.instance_methods.include?('all_hashes') ||
|
37
|
+
target.instance_methods.include?(:all_hashes)
|
38
|
+
raise "Failed to defined #{target.name}#all_hashes method. Mysql::VERSION = #{Mysql::VERSION.inspect}"
|
39
|
+
end
|
11
40
|
end
|
41
|
+
end
|
12
42
|
|
13
43
|
module ActiveRecord
|
14
44
|
class Base
|
15
|
-
|
45
|
+
def self.require_mysql
|
46
|
+
# Include the MySQL driver if one hasn't already been loaded
|
47
|
+
unless defined? Mysql
|
48
|
+
begin
|
49
|
+
require_library_or_gem 'mysql'
|
50
|
+
rescue LoadError => cannot_require_mysql
|
51
|
+
# Use the bundled Ruby/MySQL driver if no driver is already in place
|
52
|
+
begin
|
53
|
+
ActiveRecord::Base.logger.info(
|
54
|
+
"WARNING: You're using the Ruby-based MySQL library that ships with Rails. This library is not suited for production. " +
|
55
|
+
"Please install the C-based MySQL library instead (gem install mysql)."
|
56
|
+
) if ActiveRecord::Base.logger
|
57
|
+
|
58
|
+
require 'active_record/vendor/mysql'
|
59
|
+
rescue LoadError
|
60
|
+
raise cannot_require_mysql
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Define Mysql::Result.all_hashes
|
66
|
+
MysqlCompat.define_all_hashes_method!
|
67
|
+
end
|
68
|
+
|
69
|
+
# Establishes a connection to the database that's used by all Active Record objects.
|
16
70
|
def self.mysql_connection(config) # :nodoc:
|
17
|
-
|
71
|
+
config = config.symbolize_keys
|
18
72
|
host = config[:host]
|
19
73
|
port = config[:port]
|
20
74
|
socket = config[:socket]
|
21
|
-
username = config[:username]
|
22
|
-
password = config[:password]
|
75
|
+
username = config[:username] ? config[:username].to_s : 'root'
|
76
|
+
password = config[:password].to_s
|
23
77
|
|
24
78
|
if config.has_key?(:database)
|
25
79
|
database = config[:database]
|
@@ -27,105 +81,416 @@ module ActiveRecord
|
|
27
81
|
raise ArgumentError, "No database specified. Missing argument: database."
|
28
82
|
end
|
29
83
|
|
30
|
-
|
31
|
-
|
32
|
-
)
|
84
|
+
require_mysql
|
85
|
+
mysql = Mysql.init
|
86
|
+
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslkey]
|
87
|
+
|
88
|
+
ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config)
|
33
89
|
end
|
34
90
|
end
|
35
91
|
|
36
92
|
module ConnectionAdapters
|
37
|
-
class
|
38
|
-
def
|
39
|
-
|
93
|
+
class MysqlColumn < Column #:nodoc:
|
94
|
+
def extract_default(default)
|
95
|
+
if type == :binary || type == :text
|
96
|
+
if default.blank?
|
97
|
+
default
|
98
|
+
else
|
99
|
+
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
100
|
+
end
|
101
|
+
elsif missing_default_forged_as_empty_string?(default)
|
102
|
+
nil
|
103
|
+
else
|
104
|
+
super
|
105
|
+
end
|
40
106
|
end
|
41
107
|
|
42
|
-
|
43
|
-
|
44
|
-
|
108
|
+
private
|
109
|
+
def simplified_type(field_type)
|
110
|
+
return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
|
111
|
+
return :string if field_type =~ /enum/i
|
112
|
+
super
|
113
|
+
end
|
114
|
+
|
115
|
+
# MySQL misreports NOT NULL column default when none is given.
|
116
|
+
# We can't detect this for columns which may have a legitimate ''
|
117
|
+
# default (string) but we can for others (integer, datetime, boolean,
|
118
|
+
# and the rest).
|
119
|
+
#
|
120
|
+
# Test whether the column has default '', is not null, and is not
|
121
|
+
# a type allowing default ''.
|
122
|
+
def missing_default_forged_as_empty_string?(default)
|
123
|
+
type != :string && !null && default == ''
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# The MySQL adapter will work with both Ruby/MySQL, which is a Ruby-based MySQL adapter that comes bundled with Active Record, and with
|
128
|
+
# the faster C-based MySQL/Ruby adapter (available both as a gem and from http://www.tmtm.org/en/mysql/ruby/).
|
129
|
+
#
|
130
|
+
# Options:
|
131
|
+
#
|
132
|
+
# * <tt>:host</tt> -- Defaults to localhost
|
133
|
+
# * <tt>:port</tt> -- Defaults to 3306
|
134
|
+
# * <tt>:socket</tt> -- Defaults to /tmp/mysql.sock
|
135
|
+
# * <tt>:username</tt> -- Defaults to root
|
136
|
+
# * <tt>:password</tt> -- Defaults to nothing
|
137
|
+
# * <tt>:database</tt> -- The name of the database. No default, must be provided.
|
138
|
+
# * <tt>:encoding</tt> -- (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection
|
139
|
+
# * <tt>:sslkey</tt> -- Necessary to use MySQL with an SSL connection
|
140
|
+
# * <tt>:sslcert</tt> -- Necessary to use MySQL with an SSL connection
|
141
|
+
# * <tt>:sslcapath</tt> -- Necessary to use MySQL with an SSL connection
|
142
|
+
# * <tt>:sslcipher</tt> -- Necessary to use MySQL with an SSL connection
|
143
|
+
#
|
144
|
+
# By default, the MysqlAdapter will consider all columns of type tinyint(1)
|
145
|
+
# as boolean. If you wish to disable this emulation (which was the default
|
146
|
+
# behavior in versions 0.13.1 and earlier) you can add the following line
|
147
|
+
# to your environment.rb file:
|
148
|
+
#
|
149
|
+
# ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false
|
150
|
+
class MysqlAdapter < AbstractAdapter
|
151
|
+
@@emulate_booleans = true
|
152
|
+
cattr_accessor :emulate_booleans
|
153
|
+
|
154
|
+
LOST_CONNECTION_ERROR_MESSAGES = [
|
155
|
+
"Server shutdown in progress",
|
156
|
+
"Broken pipe",
|
157
|
+
"Lost connection to MySQL server during query",
|
158
|
+
"MySQL server has gone away"
|
159
|
+
]
|
160
|
+
|
161
|
+
def initialize(connection, logger, connection_options, config)
|
162
|
+
super(connection, logger)
|
163
|
+
@connection_options, @config = connection_options, config
|
164
|
+
|
165
|
+
connect
|
45
166
|
end
|
46
167
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
log(sql, name, @connection) { |connection| result = connection.query(sql) }
|
168
|
+
def adapter_name #:nodoc:
|
169
|
+
'MySQL'
|
170
|
+
end
|
51
171
|
|
52
|
-
|
53
|
-
|
54
|
-
columns
|
172
|
+
def supports_migrations? #:nodoc:
|
173
|
+
true
|
55
174
|
end
|
56
175
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
176
|
+
def native_database_types #:nodoc:
|
177
|
+
{
|
178
|
+
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
|
179
|
+
:string => { :name => "varchar", :limit => 255 },
|
180
|
+
:text => { :name => "text" },
|
181
|
+
:integer => { :name => "int", :limit => 11 },
|
182
|
+
:float => { :name => "float" },
|
183
|
+
:decimal => { :name => "decimal" },
|
184
|
+
:datetime => { :name => "datetime" },
|
185
|
+
:timestamp => { :name => "datetime" },
|
186
|
+
:time => { :name => "time" },
|
187
|
+
:date => { :name => "date" },
|
188
|
+
:binary => { :name => "blob" },
|
189
|
+
:boolean => { :name => "tinyint", :limit => 1 }
|
190
|
+
}
|
60
191
|
end
|
61
192
|
|
62
|
-
|
63
|
-
|
193
|
+
|
194
|
+
# QUOTING ==================================================
|
195
|
+
|
196
|
+
def quote(value, column = nil)
|
197
|
+
if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
198
|
+
s = column.class.string_to_binary(value).unpack("H*")[0]
|
199
|
+
"x'#{s}'"
|
200
|
+
elsif value.kind_of?(BigDecimal)
|
201
|
+
"'#{value.to_s("F")}'"
|
202
|
+
else
|
203
|
+
super
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def quote_column_name(name) #:nodoc:
|
208
|
+
"`#{name}`"
|
209
|
+
end
|
210
|
+
|
211
|
+
def quote_table_name(name) #:nodoc:
|
212
|
+
quote_column_name(name).gsub('.', '`.`')
|
213
|
+
end
|
214
|
+
|
215
|
+
def quote_string(string) #:nodoc:
|
216
|
+
@connection.quote(string)
|
217
|
+
end
|
218
|
+
|
219
|
+
def quoted_true
|
220
|
+
"1"
|
221
|
+
end
|
222
|
+
|
223
|
+
def quoted_false
|
224
|
+
"0"
|
64
225
|
end
|
65
226
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
227
|
+
# REFERENTIAL INTEGRITY ====================================
|
228
|
+
|
229
|
+
def disable_referential_integrity(&block) #:nodoc:
|
230
|
+
old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
|
231
|
+
|
70
232
|
begin
|
71
|
-
|
72
|
-
|
73
|
-
|
233
|
+
update("SET FOREIGN_KEY_CHECKS = 0")
|
234
|
+
yield
|
235
|
+
ensure
|
236
|
+
update("SET FOREIGN_KEY_CHECKS = #{old}")
|
74
237
|
end
|
75
238
|
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
239
|
+
|
240
|
+
# CONNECTION MANAGEMENT ====================================
|
241
|
+
|
242
|
+
def active?
|
243
|
+
if @connection.respond_to?(:stat)
|
244
|
+
@connection.stat
|
245
|
+
else
|
246
|
+
@connection.query 'select 1'
|
82
247
|
end
|
248
|
+
|
249
|
+
# mysql-ruby doesn't raise an exception when stat fails.
|
250
|
+
if @connection.respond_to?(:errno)
|
251
|
+
@connection.errno.zero?
|
252
|
+
else
|
253
|
+
true
|
254
|
+
end
|
255
|
+
rescue Mysql::Error
|
256
|
+
false
|
83
257
|
end
|
84
|
-
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
258
|
+
|
259
|
+
def reconnect!
|
260
|
+
disconnect!
|
261
|
+
connect
|
262
|
+
end
|
263
|
+
|
264
|
+
def disconnect!
|
265
|
+
@connection.close rescue nil
|
266
|
+
end
|
267
|
+
|
268
|
+
|
269
|
+
# DATABASE STATEMENTS ======================================
|
270
|
+
|
271
|
+
def select_rows(sql, name = nil)
|
272
|
+
@connection.query_with_result = true
|
273
|
+
result = execute(sql, name)
|
274
|
+
rows = []
|
275
|
+
result.each { |row| rows << row }
|
276
|
+
result.free
|
277
|
+
rows
|
278
|
+
end
|
279
|
+
|
280
|
+
def execute(sql, name = nil) #:nodoc:
|
281
|
+
log(sql, name) { @connection.query(sql) }
|
282
|
+
rescue ActiveRecord::StatementInvalid => exception
|
283
|
+
if exception.message.split(":").first =~ /Packets out of order/
|
284
|
+
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
|
285
|
+
else
|
286
|
+
raise
|
90
287
|
end
|
91
288
|
end
|
92
289
|
|
93
|
-
def
|
94
|
-
|
290
|
+
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
291
|
+
super sql, name
|
292
|
+
id_value || @connection.insert_id
|
293
|
+
end
|
294
|
+
|
295
|
+
def update_sql(sql, name = nil) #:nodoc:
|
296
|
+
super
|
297
|
+
@connection.affected_rows
|
298
|
+
end
|
299
|
+
|
300
|
+
def begin_db_transaction #:nodoc:
|
301
|
+
execute "BEGIN"
|
302
|
+
rescue Exception
|
303
|
+
# Transactions aren't supported
|
95
304
|
end
|
96
|
-
|
97
|
-
def
|
98
|
-
|
99
|
-
|
305
|
+
|
306
|
+
def commit_db_transaction #:nodoc:
|
307
|
+
execute "COMMIT"
|
308
|
+
rescue Exception
|
309
|
+
# Transactions aren't supported
|
310
|
+
end
|
311
|
+
|
312
|
+
def rollback_db_transaction #:nodoc:
|
313
|
+
execute "ROLLBACK"
|
314
|
+
rescue Exception
|
315
|
+
# Transactions aren't supported
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
def add_limit_offset!(sql, options) #:nodoc:
|
320
|
+
if limit = options[:limit]
|
321
|
+
unless offset = options[:offset]
|
322
|
+
sql << " LIMIT #{limit}"
|
323
|
+
else
|
324
|
+
sql << " LIMIT #{offset}, #{limit}"
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
# SCHEMA STATEMENTS ========================================
|
331
|
+
|
332
|
+
def structure_dump #:nodoc:
|
333
|
+
if supports_views?
|
334
|
+
sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
|
335
|
+
else
|
336
|
+
sql = "SHOW TABLES"
|
337
|
+
end
|
338
|
+
|
339
|
+
select_all(sql).inject("") do |structure, table|
|
340
|
+
table.delete('Table_type')
|
341
|
+
structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
|
100
342
|
end
|
101
343
|
end
|
102
|
-
|
103
|
-
def recreate_database(name)
|
344
|
+
|
345
|
+
def recreate_database(name) #:nodoc:
|
104
346
|
drop_database(name)
|
105
347
|
create_database(name)
|
106
348
|
end
|
107
|
-
|
108
|
-
|
109
|
-
|
349
|
+
|
350
|
+
# Create a new MySQL database with optional :charset and :collation.
|
351
|
+
# Charset defaults to utf8.
|
352
|
+
#
|
353
|
+
# Example:
|
354
|
+
# create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin'
|
355
|
+
# create_database 'matt_development'
|
356
|
+
# create_database 'matt_development', :charset => :big5
|
357
|
+
def create_database(name, options = {})
|
358
|
+
if options[:collation]
|
359
|
+
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
|
360
|
+
else
|
361
|
+
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def drop_database(name) #:nodoc:
|
366
|
+
execute "DROP DATABASE IF EXISTS `#{name}`"
|
367
|
+
end
|
368
|
+
|
369
|
+
def current_database
|
370
|
+
select_value 'SELECT DATABASE() as db'
|
371
|
+
end
|
372
|
+
|
373
|
+
# Returns the database character set.
|
374
|
+
def charset
|
375
|
+
show_variable 'character_set_database'
|
376
|
+
end
|
377
|
+
|
378
|
+
# Returns the database collation strategy.
|
379
|
+
def collation
|
380
|
+
show_variable 'collation_database'
|
381
|
+
end
|
382
|
+
|
383
|
+
def tables(name = nil) #:nodoc:
|
384
|
+
tables = []
|
385
|
+
execute("SHOW TABLES", name).each { |field| tables << field[0] }
|
386
|
+
tables
|
387
|
+
end
|
388
|
+
|
389
|
+
def drop_table(table_name, options = {})
|
390
|
+
super(table_name, options)
|
391
|
+
end
|
392
|
+
|
393
|
+
def indexes(table_name, name = nil)#:nodoc:
|
394
|
+
indexes = []
|
395
|
+
current_index = nil
|
396
|
+
execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name).each do |row|
|
397
|
+
if current_index != row[2]
|
398
|
+
next if row[2] == "PRIMARY" # skip the primary key
|
399
|
+
current_index = row[2]
|
400
|
+
indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
|
401
|
+
end
|
402
|
+
|
403
|
+
indexes.last.columns << row[4]
|
404
|
+
end
|
405
|
+
indexes
|
406
|
+
end
|
407
|
+
|
408
|
+
def columns(table_name, name = nil)#:nodoc:
|
409
|
+
sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
|
410
|
+
columns = []
|
411
|
+
execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
|
412
|
+
columns
|
413
|
+
end
|
414
|
+
|
415
|
+
def create_table(table_name, options = {}) #:nodoc:
|
416
|
+
super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
|
417
|
+
end
|
418
|
+
|
419
|
+
def rename_table(table_name, new_name)
|
420
|
+
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
421
|
+
end
|
422
|
+
|
423
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
424
|
+
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
425
|
+
|
426
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}")
|
427
|
+
end
|
428
|
+
|
429
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
430
|
+
unless options_include_default?(options)
|
431
|
+
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
432
|
+
options[:default] = column.default
|
433
|
+
else
|
434
|
+
raise "No such column: #{table_name}.#{column_name}"
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
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])}"
|
439
|
+
add_column_options!(change_column_sql, options)
|
440
|
+
execute(change_column_sql)
|
441
|
+
end
|
442
|
+
|
443
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
444
|
+
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
445
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
446
|
+
end
|
447
|
+
|
448
|
+
|
449
|
+
# SHOW VARIABLES LIKE 'name'
|
450
|
+
def show_variable(name)
|
451
|
+
variables = select_all("SHOW VARIABLES LIKE '#{name}'")
|
452
|
+
variables.first['Value'] unless variables.empty?
|
110
453
|
end
|
111
|
-
|
112
|
-
|
113
|
-
|
454
|
+
|
455
|
+
# Returns a table's primary key and belonging sequence.
|
456
|
+
def pk_and_sequence_for(table) #:nodoc:
|
457
|
+
keys = []
|
458
|
+
execute("describe #{quote_table_name(table)}").each_hash do |h|
|
459
|
+
keys << h["Field"]if h["Key"] == "PRI"
|
460
|
+
end
|
461
|
+
keys.length == 1 ? [keys.first, nil] : nil
|
114
462
|
end
|
115
|
-
|
463
|
+
|
116
464
|
private
|
465
|
+
def connect
|
466
|
+
encoding = @config[:encoding]
|
467
|
+
if encoding
|
468
|
+
@connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
|
469
|
+
end
|
470
|
+
@connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher]) if @config[:sslkey]
|
471
|
+
@connection.real_connect(*@connection_options)
|
472
|
+
execute("SET NAMES '#{encoding}'") if encoding
|
473
|
+
|
474
|
+
# By default, MySQL 'where id is null' selects the last inserted id.
|
475
|
+
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
476
|
+
execute("SET SQL_AUTO_IS_NULL=0")
|
477
|
+
end
|
478
|
+
|
117
479
|
def select(sql, name = nil)
|
118
|
-
|
119
|
-
|
120
|
-
rows =
|
121
|
-
|
122
|
-
result.each_hash { |row| rows << all_fields_initialized.dup.update(row) }
|
480
|
+
@connection.query_with_result = true
|
481
|
+
result = execute(sql, name)
|
482
|
+
rows = result.all_hashes
|
483
|
+
result.free
|
123
484
|
rows
|
124
485
|
end
|
486
|
+
|
487
|
+
def supports_views?
|
488
|
+
version[0] >= 5
|
489
|
+
end
|
490
|
+
|
491
|
+
def version
|
492
|
+
@version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
493
|
+
end
|
125
494
|
end
|
126
495
|
end
|
127
496
|
end
|
128
|
-
|
129
|
-
rescue LoadError
|
130
|
-
# MySQL is not available, so neither should the adapter be
|
131
|
-
end
|