activerecord 5.0.0.beta3 → 5.0.0.beta4

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.

Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +225 -8
  3. data/examples/performance.rb +0 -1
  4. data/examples/simple.rb +0 -1
  5. data/lib/active_record.rb +0 -1
  6. data/lib/active_record/associations.rb +10 -6
  7. data/lib/active_record/associations/association.rb +1 -1
  8. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  9. data/lib/active_record/associations/join_dependency.rb +1 -1
  10. data/lib/active_record/associations/preloader.rb +1 -0
  11. data/lib/active_record/associations/preloader/through_association.rb +15 -8
  12. data/lib/active_record/attribute/user_provided_default.rb +10 -5
  13. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  14. data/lib/active_record/attributes.rb +3 -3
  15. data/lib/active_record/autosave_association.rb +1 -1
  16. data/lib/active_record/base.rb +2 -1
  17. data/lib/active_record/collection_cache_key.rb +1 -1
  18. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +0 -19
  19. data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -8
  20. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  21. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -1
  22. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  23. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +16 -2
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -4
  25. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +20 -10
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -20
  27. data/lib/active_record/connection_adapters/abstract/transaction.rb +13 -1
  28. data/lib/active_record/connection_adapters/abstract_adapter.rb +37 -16
  29. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +119 -108
  30. data/lib/active_record/connection_adapters/column.rb +5 -6
  31. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  32. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  33. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +27 -6
  34. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -13
  35. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -55
  36. data/lib/active_record/connection_adapters/postgresql/column.rb +0 -1
  37. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  38. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  39. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +7 -10
  40. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -25
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +59 -30
  42. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  43. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +7 -0
  44. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +19 -56
  45. data/lib/active_record/connection_adapters/statement_pool.rb +6 -4
  46. data/lib/active_record/core.rb +13 -4
  47. data/lib/active_record/enum.rb +1 -1
  48. data/lib/active_record/errors.rb +9 -0
  49. data/lib/active_record/fixture_set/file.rb +7 -1
  50. data/lib/active_record/gem_version.rb +1 -1
  51. data/lib/active_record/locking/optimistic.rb +4 -0
  52. data/lib/active_record/log_subscriber.rb +1 -1
  53. data/lib/active_record/migration.rb +4 -4
  54. data/lib/active_record/migration/compatibility.rb +1 -1
  55. data/lib/active_record/model_schema.rb +12 -0
  56. data/lib/active_record/nested_attributes.rb +1 -1
  57. data/lib/active_record/persistence.rb +1 -1
  58. data/lib/active_record/query_cache.rb +13 -16
  59. data/lib/active_record/querying.rb +1 -1
  60. data/lib/active_record/railtie.rb +8 -11
  61. data/lib/active_record/railties/databases.rake +5 -5
  62. data/lib/active_record/reflection.rb +21 -4
  63. data/lib/active_record/relation.rb +10 -10
  64. data/lib/active_record/relation/batches.rb +29 -9
  65. data/lib/active_record/relation/delegation.rb +0 -1
  66. data/lib/active_record/relation/finder_methods.rb +27 -8
  67. data/lib/active_record/relation/predicate_builder.rb +4 -2
  68. data/lib/active_record/relation/where_clause.rb +2 -1
  69. data/lib/active_record/schema_dumper.rb +39 -24
  70. data/lib/active_record/scoping/default.rb +2 -1
  71. data/lib/active_record/suppressor.rb +5 -1
  72. data/lib/active_record/tasks/database_tasks.rb +6 -4
  73. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  74. data/lib/active_record/type.rb +1 -1
  75. data/lib/active_record/type/internal/abstract_json.rb +1 -5
  76. data/lib/active_record/type/time.rb +12 -0
  77. data/lib/active_record/validations/uniqueness.rb +1 -4
  78. data/lib/rails/generators/active_record/model/model_generator.rb +15 -11
  79. data/lib/rails/generators/active_record/model/templates/application_record.rb +2 -0
  80. metadata +11 -8
@@ -1,11 +1,9 @@
1
- require 'set'
2
-
3
1
  module ActiveRecord
4
2
  # :stopdoc:
5
3
  module ConnectionAdapters
6
4
  # An abstract definition of a column in a table.
7
5
  class Column
8
- attr_reader :name, :null, :sql_type_metadata, :default, :default_function, :collation
6
+ attr_reader :name, :default, :sql_type_metadata, :null, :table_name, :default_function, :collation, :comment
9
7
 
10
8
  delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
11
9
 
@@ -15,14 +13,15 @@ module ActiveRecord
15
13
  # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
16
14
  # +sql_type_metadata+ is various information about the type of the column
17
15
  # +null+ determines if this column allows +NULL+ values.
18
- def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation = nil)
16
+ def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil)
19
17
  @name = name.freeze
18
+ @table_name = table_name
20
19
  @sql_type_metadata = sql_type_metadata
21
20
  @null = null
22
21
  @default = default
23
22
  @default_function = default_function
24
23
  @collation = collation
25
- @table_name = nil
24
+ @comment = comment
26
25
  end
27
26
 
28
27
  def has_default?
@@ -54,7 +53,7 @@ module ActiveRecord
54
53
  protected
55
54
 
56
55
  def attributes_for_hash
57
- [self.class, name, default, sql_type_metadata, null, default_function, collation]
56
+ [self.class, name, default, sql_type_metadata, null, table_name, default_function, collation]
58
57
  end
59
58
  end
60
59
 
@@ -0,0 +1,125 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MySQL
4
+ module DatabaseStatements
5
+ # Returns an ActiveRecord::Result instance.
6
+ def select_all(arel, name = nil, binds = [], preparable: nil)
7
+ result = if ExplainRegistry.collect? && prepared_statements
8
+ unprepared_statement { super }
9
+ else
10
+ super
11
+ end
12
+ @connection.next_result while @connection.more_results?
13
+ result
14
+ end
15
+
16
+ # Returns a record hash with the column names as keys and column values
17
+ # as values.
18
+ def select_one(arel, name = nil, binds = [])
19
+ arel, binds = binds_from_relation(arel, binds)
20
+ @connection.query_options.merge!(as: :hash)
21
+ select_result(to_sql(arel, binds), name, binds) do |result|
22
+ @connection.next_result while @connection.more_results?
23
+ result.first
24
+ end
25
+ ensure
26
+ @connection.query_options.merge!(as: :array)
27
+ end
28
+
29
+ # Returns an array of arrays containing the field values.
30
+ # Order is the same as that returned by +columns+.
31
+ def select_rows(sql, name = nil, binds = [])
32
+ select_result(sql, name, binds) do |result|
33
+ @connection.next_result while @connection.more_results?
34
+ result.to_a
35
+ end
36
+ end
37
+
38
+ # Executes the SQL statement in the context of this connection.
39
+ def execute(sql, name = nil)
40
+ if @connection
41
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
42
+ # made since we established the connection
43
+ @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
44
+ end
45
+
46
+ super
47
+ end
48
+
49
+ def exec_query(sql, name = 'SQL', binds = [], prepare: false)
50
+ if without_prepared_statement?(binds)
51
+ execute_and_free(sql, name) do |result|
52
+ ActiveRecord::Result.new(result.fields, result.to_a) if result
53
+ end
54
+ else
55
+ exec_stmt_and_free(sql, name, binds, cache_stmt: prepare) do |_, result|
56
+ ActiveRecord::Result.new(result.fields, result.to_a) if result
57
+ end
58
+ end
59
+ end
60
+
61
+ def exec_delete(sql, name, binds)
62
+ if without_prepared_statement?(binds)
63
+ execute_and_free(sql, name) { @connection.affected_rows }
64
+ else
65
+ exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
66
+ end
67
+ end
68
+ alias :exec_update :exec_delete
69
+
70
+ protected
71
+
72
+ def last_inserted_id(result)
73
+ @connection.last_id
74
+ end
75
+
76
+ private
77
+
78
+ def select_result(sql, name = nil, binds = [])
79
+ if without_prepared_statement?(binds)
80
+ execute_and_free(sql, name) { |result| yield result }
81
+ else
82
+ exec_stmt_and_free(sql, name, binds, cache_stmt: true) { |_, result| yield result }
83
+ end
84
+ end
85
+
86
+ def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
87
+ if @connection
88
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
89
+ # made since we established the connection
90
+ @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
91
+ end
92
+
93
+ type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
94
+
95
+ log(sql, name, binds) do
96
+ if cache_stmt
97
+ cache = @statements[sql] ||= {
98
+ stmt: @connection.prepare(sql)
99
+ }
100
+ stmt = cache[:stmt]
101
+ else
102
+ stmt = @connection.prepare(sql)
103
+ end
104
+
105
+ begin
106
+ result = stmt.execute(*type_casted_binds)
107
+ rescue Mysql2::Error => e
108
+ if cache_stmt
109
+ @statements.delete(sql)
110
+ else
111
+ stmt.close
112
+ end
113
+ raise e
114
+ end
115
+
116
+ ret = yield stmt, result
117
+ result.free if result
118
+ stmt.close unless cache_stmt
119
+ ret
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,51 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MySQL
4
+ module Quoting # :nodoc:
5
+ QUOTED_TRUE, QUOTED_FALSE = '1', '0'
6
+
7
+ def quote_column_name(name)
8
+ @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
9
+ end
10
+
11
+ def quote_table_name(name)
12
+ @quoted_table_names[name] ||= super.gsub('.', '`.`')
13
+ end
14
+
15
+ def quoted_true
16
+ QUOTED_TRUE
17
+ end
18
+
19
+ def unquoted_true
20
+ 1
21
+ end
22
+
23
+ def quoted_false
24
+ QUOTED_FALSE
25
+ end
26
+
27
+ def unquoted_false
28
+ 0
29
+ end
30
+
31
+ def quoted_date(value)
32
+ if supports_datetime_with_precision?
33
+ super
34
+ else
35
+ super.sub(/\.\d{6}\z/, '')
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def _quote(value)
42
+ if value.is_a?(Type::Binary::Data)
43
+ "x'#{value.hex}'"
44
+ else
45
+ super
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -2,6 +2,9 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module MySQL
4
4
  class SchemaCreation < AbstractAdapter::SchemaCreation
5
+ delegate :quote, to: :@conn
6
+ private :quote
7
+
5
8
  private
6
9
 
7
10
  def visit_DropForeignKey(name)
@@ -22,6 +25,14 @@ module ActiveRecord
22
25
  add_column_position!(change_column_sql, column_options(o.column))
23
26
  end
24
27
 
28
+ def add_table_options!(create_sql, options)
29
+ super
30
+
31
+ if comment = options[:comment]
32
+ create_sql << " COMMENT #{quote(comment)}"
33
+ end
34
+ end
35
+
25
36
  def column_options(o)
26
37
  column_options = super
27
38
  column_options[:charset] = o.charset
@@ -29,13 +40,21 @@ module ActiveRecord
29
40
  end
30
41
 
31
42
  def add_column_options!(sql, options)
32
- if options[:charset]
33
- sql << " CHARACTER SET #{options[:charset]}"
43
+ if charset = options[:charset]
44
+ sql << " CHARACTER SET #{charset}"
34
45
  end
35
- if options[:collation]
36
- sql << " COLLATE #{options[:collation]}"
46
+
47
+ if collation = options[:collation]
48
+ sql << " COLLATE #{collation}"
37
49
  end
50
+
38
51
  super
52
+
53
+ if comment = options[:comment]
54
+ sql << " COMMENT #{quote(comment)}"
55
+ end
56
+
57
+ sql
39
58
  end
40
59
 
41
60
  def add_column_position!(sql, options)
@@ -44,12 +63,14 @@ module ActiveRecord
44
63
  elsif options[:after]
45
64
  sql << " AFTER #{quote_column_name(options[:after])}"
46
65
  end
66
+
47
67
  sql
48
68
  end
49
69
 
50
70
  def index_in_create(table_name, column_name, options)
51
- index_name, index_type, index_columns, _, _, index_using = @conn.add_index_options(table_name, column_name, options)
52
- "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns}) "
71
+ index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
72
+ index_option = " COMMENT #{quote(comment)}" if comment
73
+ "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_option} "
53
74
  end
54
75
  end
55
76
  end
@@ -3,18 +3,13 @@ module ActiveRecord
3
3
  module MySQL
4
4
  module ColumnDumper
5
5
  def column_spec_for_primary_key(column)
6
- spec = {}
7
6
  if column.bigint?
8
- spec[:id] = ':bigint'
7
+ spec = { id: :bigint.inspect }
9
8
  spec[:default] = schema_default(column) || 'nil' unless column.auto_increment?
10
- spec[:unsigned] = 'true' if column.unsigned?
11
- elsif column.auto_increment?
12
- spec[:unsigned] = 'true' if column.unsigned?
13
- return if spec.empty?
14
9
  else
15
- spec[:id] = schema_type(column).inspect
16
- spec.merge!(prepare_column_options(column).delete_if { |key, _| [:name, :type, :null].include?(key) })
10
+ spec = super
17
11
  end
12
+ spec[:unsigned] = 'true' if column.unsigned?
18
13
  spec
19
14
  end
20
15
 
@@ -30,6 +25,10 @@ module ActiveRecord
30
25
 
31
26
  private
32
27
 
28
+ def default_primary_key?(column)
29
+ super && column.auto_increment?
30
+ end
31
+
33
32
  def schema_type(column)
34
33
  if column.sql_type == 'tinyblob'
35
34
  :blob
@@ -38,16 +37,12 @@ module ActiveRecord
38
37
  end
39
38
  end
40
39
 
41
- def schema_limit(column)
42
- super unless column.type == :boolean
43
- end
44
-
45
40
  def schema_precision(column)
46
41
  super unless /time/ === column.sql_type && column.precision == 0
47
42
  end
48
43
 
49
44
  def schema_collation(column)
50
- if column.collation && table_name = column.instance_variable_get(:@table_name)
45
+ if column.collation && table_name = column.table_name
51
46
  @table_collation_cache ||= {}
52
47
  @table_collation_cache[table_name] ||= select_one("SHOW TABLE STATUS LIKE '#{table_name}'")["Collation"]
53
48
  column.collation.inspect if column.collation != @table_collation_cache[table_name]
@@ -1,7 +1,9 @@
1
1
  require 'active_record/connection_adapters/abstract_mysql_adapter'
2
+ require 'active_record/connection_adapters/mysql/database_statements'
2
3
 
3
4
  gem 'mysql2', '>= 0.3.18', '< 0.5'
4
5
  require 'mysql2'
6
+ raise 'mysql2 0.4.3 is not supported. Please upgrade to 0.4.4+' if Mysql2::VERSION == '0.4.3'
5
7
 
6
8
  module ActiveRecord
7
9
  module ConnectionHandling # :nodoc:
@@ -16,7 +18,7 @@ module ActiveRecord
16
18
  if config[:flags].kind_of? Array
17
19
  config[:flags].push "FOUND_ROWS".freeze
18
20
  else
19
- config[:flags] |= Mysql2::Client::FOUND_ROWS
21
+ config[:flags] |= Mysql2::Client::FOUND_ROWS
20
22
  end
21
23
  end
22
24
 
@@ -35,9 +37,11 @@ module ActiveRecord
35
37
  class Mysql2Adapter < AbstractMysqlAdapter
36
38
  ADAPTER_NAME = 'Mysql2'.freeze
37
39
 
40
+ include MySQL::DatabaseStatements
41
+
38
42
  def initialize(connection, logger, connection_options, config)
39
43
  super
40
- @prepared_statements = false
44
+ @prepared_statements = false unless config.key?(:prepared_statements)
41
45
  configure_connection
42
46
  end
43
47
 
@@ -45,6 +49,18 @@ module ActiveRecord
45
49
  !mariadb? && version >= '5.7.8'
46
50
  end
47
51
 
52
+ def supports_comments?
53
+ true
54
+ end
55
+
56
+ def supports_comments_in_create?
57
+ true
58
+ end
59
+
60
+ def supports_savepoints?
61
+ true
62
+ end
63
+
48
64
  # HELPER METHODS ===========================================
49
65
 
50
66
  def each_hash(result) # :nodoc:
@@ -95,59 +111,6 @@ module ActiveRecord
95
111
  end
96
112
  end
97
113
 
98
- #--
99
- # DATABASE STATEMENTS ======================================
100
- #++
101
-
102
- # Returns a record hash with the column names as keys and column values
103
- # as values.
104
- def select_one(arel, name = nil, binds = [])
105
- arel, binds = binds_from_relation(arel, binds)
106
- execute(to_sql(arel, binds), name).each(as: :hash) do |row|
107
- @connection.next_result while @connection.more_results?
108
- return row
109
- end
110
- end
111
-
112
- # Returns an array of arrays containing the field values.
113
- # Order is the same as that returned by +columns+.
114
- def select_rows(sql, name = nil, binds = [])
115
- result = execute(sql, name)
116
- @connection.next_result while @connection.more_results?
117
- result.to_a
118
- end
119
-
120
- # Executes the SQL statement in the context of this connection.
121
- def execute(sql, name = nil)
122
- if @connection
123
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
124
- # made since we established the connection
125
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
126
- end
127
-
128
- super
129
- end
130
-
131
- def exec_query(sql, name = 'SQL', binds = [], prepare: false)
132
- result = execute(sql, name)
133
- @connection.next_result while @connection.more_results?
134
- ActiveRecord::Result.new(result.fields, result.to_a)
135
- end
136
-
137
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
138
- execute to_sql(sql, binds), name
139
- end
140
-
141
- def exec_delete(sql, name, binds)
142
- execute to_sql(sql, binds), name
143
- @connection.affected_rows
144
- end
145
- alias :exec_update :exec_delete
146
-
147
- def last_inserted_id(result)
148
- @connection.last_id
149
- end
150
-
151
114
  private
152
115
 
153
116
  def connect
@@ -8,7 +8,6 @@ module ActiveRecord
8
8
  def serial?
9
9
  return unless default_function
10
10
 
11
- table_name = @table_name || '(?<table_name>.+)'
12
11
  %r{\Anextval\('"?#{table_name}_#{name}_seq"?'::regclass\)\z} === default_function
13
12
  end
14
13
  end