activerecord 5.2.3

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 (244) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +937 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +217 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +188 -0
  8. data/lib/active_record/aggregations.rb +283 -0
  9. data/lib/active_record/association_relation.rb +40 -0
  10. data/lib/active_record/associations.rb +1860 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +299 -0
  13. data/lib/active_record/associations/association_scope.rb +168 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +130 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +140 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +163 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +82 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +135 -0
  20. data/lib/active_record/associations/builder/has_many.rb +17 -0
  21. data/lib/active_record/associations/builder/has_one.rb +30 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +42 -0
  23. data/lib/active_record/associations/collection_association.rb +513 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1131 -0
  25. data/lib/active_record/associations/foreign_association.rb +13 -0
  26. data/lib/active_record/associations/has_many_association.rb +144 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +227 -0
  28. data/lib/active_record/associations/has_one_association.rb +120 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +262 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +60 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +193 -0
  35. data/lib/active_record/associations/preloader/association.rb +131 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +107 -0
  37. data/lib/active_record/associations/singular_association.rb +73 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +88 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +492 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +78 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +150 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +143 -0
  45. data/lib/active_record/attribute_methods/query.rb +42 -0
  46. data/lib/active_record/attribute_methods/read.rb +85 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +68 -0
  50. data/lib/active_record/attributes.rb +266 -0
  51. data/lib/active_record/autosave_association.rb +498 -0
  52. data/lib/active_record/base.rb +329 -0
  53. data/lib/active_record/callbacks.rb +353 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/collection_cache_key.rb +53 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1068 -0
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +72 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +540 -0
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +145 -0
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +200 -0
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +685 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1396 -0
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +628 -0
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +887 -0
  70. data/lib/active_record/connection_adapters/column.rb +91 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  73. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +129 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +863 -0
  118. data/lib/active_record/connection_adapters/schema_cache.rb +118 -0
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +573 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +145 -0
  129. data/lib/active_record/core.rb +559 -0
  130. data/lib/active_record/counter_cache.rb +218 -0
  131. data/lib/active_record/define_callbacks.rb +22 -0
  132. data/lib/active_record/dynamic_matchers.rb +122 -0
  133. data/lib/active_record/enum.rb +244 -0
  134. data/lib/active_record/errors.rb +380 -0
  135. data/lib/active_record/explain.rb +50 -0
  136. data/lib/active_record/explain_registry.rb +32 -0
  137. data/lib/active_record/explain_subscriber.rb +34 -0
  138. data/lib/active_record/fixture_set/file.rb +82 -0
  139. data/lib/active_record/fixtures.rb +1065 -0
  140. data/lib/active_record/gem_version.rb +17 -0
  141. data/lib/active_record/inheritance.rb +283 -0
  142. data/lib/active_record/integration.rb +155 -0
  143. data/lib/active_record/internal_metadata.rb +45 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  145. data/lib/active_record/locale/en.yml +48 -0
  146. data/lib/active_record/locking/optimistic.rb +198 -0
  147. data/lib/active_record/locking/pessimistic.rb +89 -0
  148. data/lib/active_record/log_subscriber.rb +137 -0
  149. data/lib/active_record/migration.rb +1378 -0
  150. data/lib/active_record/migration/command_recorder.rb +240 -0
  151. data/lib/active_record/migration/compatibility.rb +217 -0
  152. data/lib/active_record/migration/join_table.rb +17 -0
  153. data/lib/active_record/model_schema.rb +521 -0
  154. data/lib/active_record/nested_attributes.rb +600 -0
  155. data/lib/active_record/no_touching.rb +58 -0
  156. data/lib/active_record/null_relation.rb +68 -0
  157. data/lib/active_record/persistence.rb +763 -0
  158. data/lib/active_record/query_cache.rb +45 -0
  159. data/lib/active_record/querying.rb +70 -0
  160. data/lib/active_record/railtie.rb +226 -0
  161. data/lib/active_record/railties/console_sandbox.rb +7 -0
  162. data/lib/active_record/railties/controller_runtime.rb +56 -0
  163. data/lib/active_record/railties/databases.rake +377 -0
  164. data/lib/active_record/readonly_attributes.rb +24 -0
  165. data/lib/active_record/reflection.rb +1044 -0
  166. data/lib/active_record/relation.rb +629 -0
  167. data/lib/active_record/relation/batches.rb +287 -0
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  169. data/lib/active_record/relation/calculations.rb +417 -0
  170. data/lib/active_record/relation/delegation.rb +147 -0
  171. data/lib/active_record/relation/finder_methods.rb +565 -0
  172. data/lib/active_record/relation/from_clause.rb +26 -0
  173. data/lib/active_record/relation/merger.rb +193 -0
  174. data/lib/active_record/relation/predicate_builder.rb +152 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  180. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  181. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  182. data/lib/active_record/relation/query_attribute.rb +45 -0
  183. data/lib/active_record/relation/query_methods.rb +1231 -0
  184. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  185. data/lib/active_record/relation/spawn_methods.rb +77 -0
  186. data/lib/active_record/relation/where_clause.rb +186 -0
  187. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  188. data/lib/active_record/result.rb +149 -0
  189. data/lib/active_record/runtime_registry.rb +24 -0
  190. data/lib/active_record/sanitization.rb +222 -0
  191. data/lib/active_record/schema.rb +70 -0
  192. data/lib/active_record/schema_dumper.rb +255 -0
  193. data/lib/active_record/schema_migration.rb +56 -0
  194. data/lib/active_record/scoping.rb +106 -0
  195. data/lib/active_record/scoping/default.rb +152 -0
  196. data/lib/active_record/scoping/named.rb +213 -0
  197. data/lib/active_record/secure_token.rb +40 -0
  198. data/lib/active_record/serialization.rb +22 -0
  199. data/lib/active_record/statement_cache.rb +121 -0
  200. data/lib/active_record/store.rb +211 -0
  201. data/lib/active_record/suppressor.rb +61 -0
  202. data/lib/active_record/table_metadata.rb +82 -0
  203. data/lib/active_record/tasks/database_tasks.rb +337 -0
  204. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  205. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  206. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  207. data/lib/active_record/timestamp.rb +153 -0
  208. data/lib/active_record/touch_later.rb +64 -0
  209. data/lib/active_record/transactions.rb +502 -0
  210. data/lib/active_record/translation.rb +24 -0
  211. data/lib/active_record/type.rb +79 -0
  212. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  213. data/lib/active_record/type/date.rb +9 -0
  214. data/lib/active_record/type/date_time.rb +9 -0
  215. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  217. data/lib/active_record/type/internal/timezone.rb +17 -0
  218. data/lib/active_record/type/json.rb +30 -0
  219. data/lib/active_record/type/serialized.rb +71 -0
  220. data/lib/active_record/type/text.rb +11 -0
  221. data/lib/active_record/type/time.rb +21 -0
  222. data/lib/active_record/type/type_map.rb +62 -0
  223. data/lib/active_record/type/unsigned_integer.rb +17 -0
  224. data/lib/active_record/type_caster.rb +9 -0
  225. data/lib/active_record/type_caster/connection.rb +33 -0
  226. data/lib/active_record/type_caster/map.rb +23 -0
  227. data/lib/active_record/validations.rb +93 -0
  228. data/lib/active_record/validations/absence.rb +25 -0
  229. data/lib/active_record/validations/associated.rb +60 -0
  230. data/lib/active_record/validations/length.rb +26 -0
  231. data/lib/active_record/validations/presence.rb +68 -0
  232. data/lib/active_record/validations/uniqueness.rb +238 -0
  233. data/lib/active_record/version.rb +10 -0
  234. data/lib/rails/generators/active_record.rb +19 -0
  235. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  236. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  237. data/lib/rails/generators/active_record/migration.rb +35 -0
  238. data/lib/rails/generators/active_record/migration/migration_generator.rb +78 -0
  239. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  240. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  241. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  242. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  243. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  244. metadata +333 -0
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module MySQL
6
+ class ExplainPrettyPrinter # :nodoc:
7
+ # Pretty prints the result of an EXPLAIN in a way that resembles the output of the
8
+ # MySQL shell:
9
+ #
10
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
11
+ # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
12
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
13
+ # | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
14
+ # | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
15
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
16
+ # 2 rows in set (0.00 sec)
17
+ #
18
+ # This is an exercise in Ruby hyperrealism :).
19
+ def pp(result, elapsed)
20
+ widths = compute_column_widths(result)
21
+ separator = build_separator(widths)
22
+
23
+ pp = []
24
+
25
+ pp << separator
26
+ pp << build_cells(result.columns, widths)
27
+ pp << separator
28
+
29
+ result.rows.each do |row|
30
+ pp << build_cells(row, widths)
31
+ end
32
+
33
+ pp << separator
34
+ pp << build_footer(result.rows.length, elapsed)
35
+
36
+ pp.join("\n") + "\n"
37
+ end
38
+
39
+ private
40
+
41
+ def compute_column_widths(result)
42
+ [].tap do |widths|
43
+ result.columns.each_with_index do |column, i|
44
+ cells_in_column = [column] + result.rows.map { |r| r[i].nil? ? "NULL" : r[i].to_s }
45
+ widths << cells_in_column.map(&:length).max
46
+ end
47
+ end
48
+ end
49
+
50
+ def build_separator(widths)
51
+ padding = 1
52
+ "+" + widths.map { |w| "-" * (w + (padding * 2)) }.join("+") + "+"
53
+ end
54
+
55
+ def build_cells(items, widths)
56
+ cells = []
57
+ items.each_with_index do |item, i|
58
+ item = "NULL" if item.nil?
59
+ justifier = item.is_a?(Numeric) ? "rjust" : "ljust"
60
+ cells << item.to_s.send(justifier, widths[i])
61
+ end
62
+ "| " + cells.join(" | ") + " |"
63
+ end
64
+
65
+ def build_footer(nrows, elapsed)
66
+ rows_label = nrows == 1 ? "row" : "rows"
67
+ "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module MySQL
6
+ module Quoting # :nodoc:
7
+ def quote_column_name(name)
8
+ @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`".freeze
9
+ end
10
+
11
+ def quote_table_name(name)
12
+ @quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
13
+ end
14
+
15
+ def unquoted_true
16
+ 1
17
+ end
18
+
19
+ def unquoted_false
20
+ 0
21
+ end
22
+
23
+ def quoted_date(value)
24
+ if supports_datetime_with_precision?
25
+ super
26
+ else
27
+ super.sub(/\.\d{6}\z/, "")
28
+ end
29
+ end
30
+
31
+ def quoted_binary(value)
32
+ "x'#{value.hex}'"
33
+ end
34
+
35
+ def _type_cast(value)
36
+ case value
37
+ when Date, Time then value
38
+ else super
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module MySQL
6
+ class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
7
+ delegate :add_sql_comment!, :mariadb?, to: :@conn
8
+ private :add_sql_comment!, :mariadb?
9
+
10
+ private
11
+
12
+ def visit_DropForeignKey(name)
13
+ "DROP FOREIGN KEY #{name}"
14
+ end
15
+
16
+ def visit_AddColumnDefinition(o)
17
+ add_column_position!(super, column_options(o.column))
18
+ end
19
+
20
+ def visit_ChangeColumnDefinition(o)
21
+ change_column_sql = "CHANGE #{quote_column_name(o.name)} #{accept(o.column)}".dup
22
+ add_column_position!(change_column_sql, column_options(o.column))
23
+ end
24
+
25
+ def add_table_options!(create_sql, options)
26
+ add_sql_comment!(super, options[:comment])
27
+ end
28
+
29
+ def add_column_options!(sql, options)
30
+ # By default, TIMESTAMP columns are NOT NULL, cannot contain NULL values,
31
+ # and assigning NULL assigns the current timestamp. To permit a TIMESTAMP
32
+ # column to contain NULL, explicitly declare it with the NULL attribute.
33
+ # See https://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html
34
+ if /\Atimestamp\b/.match?(options[:column].sql_type) && !options[:primary_key]
35
+ sql << " NULL" unless options[:null] == false || options_include_default?(options)
36
+ end
37
+
38
+ if charset = options[:charset]
39
+ sql << " CHARACTER SET #{charset}"
40
+ end
41
+
42
+ if collation = options[:collation]
43
+ sql << " COLLATE #{collation}"
44
+ end
45
+
46
+ if as = options[:as]
47
+ sql << " AS (#{as})"
48
+ if options[:stored]
49
+ sql << (mariadb? ? " PERSISTENT" : " STORED")
50
+ end
51
+ end
52
+
53
+ add_sql_comment!(super, options[:comment])
54
+ end
55
+
56
+ def add_column_position!(sql, options)
57
+ if options[:first]
58
+ sql << " FIRST"
59
+ elsif options[:after]
60
+ sql << " AFTER #{quote_column_name(options[:after])}"
61
+ end
62
+
63
+ sql
64
+ end
65
+
66
+ def index_in_create(table_name, column_name, options)
67
+ index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
68
+ add_sql_comment!("#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})".dup, comment)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module MySQL
6
+ module ColumnMethods
7
+ def blob(*args, **options)
8
+ args.each { |name| column(name, :blob, options) }
9
+ end
10
+
11
+ def tinyblob(*args, **options)
12
+ args.each { |name| column(name, :tinyblob, options) }
13
+ end
14
+
15
+ def mediumblob(*args, **options)
16
+ args.each { |name| column(name, :mediumblob, options) }
17
+ end
18
+
19
+ def longblob(*args, **options)
20
+ args.each { |name| column(name, :longblob, options) }
21
+ end
22
+
23
+ def tinytext(*args, **options)
24
+ args.each { |name| column(name, :tinytext, options) }
25
+ end
26
+
27
+ def mediumtext(*args, **options)
28
+ args.each { |name| column(name, :mediumtext, options) }
29
+ end
30
+
31
+ def longtext(*args, **options)
32
+ args.each { |name| column(name, :longtext, options) }
33
+ end
34
+
35
+ def unsigned_integer(*args, **options)
36
+ args.each { |name| column(name, :unsigned_integer, options) }
37
+ end
38
+
39
+ def unsigned_bigint(*args, **options)
40
+ args.each { |name| column(name, :unsigned_bigint, options) }
41
+ end
42
+
43
+ def unsigned_float(*args, **options)
44
+ args.each { |name| column(name, :unsigned_float, options) }
45
+ end
46
+
47
+ def unsigned_decimal(*args, **options)
48
+ args.each { |name| column(name, :unsigned_decimal, options) }
49
+ end
50
+ end
51
+
52
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
53
+ include ColumnMethods
54
+
55
+ def new_column_definition(name, type, **options) # :nodoc:
56
+ case type
57
+ when :virtual
58
+ type = options[:type]
59
+ when :primary_key
60
+ type = :integer
61
+ options[:limit] ||= 8
62
+ options[:primary_key] = true
63
+ when /\Aunsigned_(?<type>.+)\z/
64
+ type = $~[:type].to_sym
65
+ options[:unsigned] = true
66
+ end
67
+
68
+ super
69
+ end
70
+
71
+ private
72
+ def aliased_types(name, fallback)
73
+ fallback
74
+ end
75
+
76
+ def integer_like_primary_key_type(type, options)
77
+ options[:auto_increment] = true
78
+ type
79
+ end
80
+ end
81
+
82
+ class Table < ActiveRecord::ConnectionAdapters::Table
83
+ include ColumnMethods
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module MySQL
6
+ class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
7
+ private
8
+ def prepare_column_options(column)
9
+ spec = super
10
+ spec[:unsigned] = "true" if column.unsigned?
11
+ spec[:auto_increment] = "true" if column.auto_increment?
12
+
13
+ if @connection.supports_virtual_columns? && column.virtual?
14
+ spec[:as] = extract_expression_for_virtual_column(column)
15
+ spec[:stored] = "true" if /\b(?:STORED|PERSISTENT)\b/.match?(column.extra)
16
+ spec = { type: schema_type(column).inspect }.merge!(spec)
17
+ end
18
+
19
+ spec
20
+ end
21
+
22
+ def column_spec_for_primary_key(column)
23
+ spec = super
24
+ spec.delete(:auto_increment) if column.type == :integer && column.auto_increment?
25
+ spec
26
+ end
27
+
28
+ def default_primary_key?(column)
29
+ super && column.auto_increment? && !column.unsigned?
30
+ end
31
+
32
+ def explicit_primary_key_default?(column)
33
+ column.type == :integer && !column.auto_increment?
34
+ end
35
+
36
+ def schema_type(column)
37
+ case column.sql_type
38
+ when /\Atimestamp\b/
39
+ :timestamp
40
+ when "tinyblob"
41
+ :blob
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ def schema_precision(column)
48
+ super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
49
+ end
50
+
51
+ def schema_collation(column)
52
+ if column.collation && table_name = column.table_name
53
+ @table_collation_cache ||= {}
54
+ @table_collation_cache[table_name] ||=
55
+ @connection.exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
56
+ column.collation.inspect if column.collation != @table_collation_cache[table_name]
57
+ end
58
+ end
59
+
60
+ def extract_expression_for_virtual_column(column)
61
+ if @connection.mariadb? && @connection.version < "10.2.5"
62
+ create_table_info = @connection.send(:create_table_info, column.table_name)
63
+ column_name = @connection.quote_column_name(column.name)
64
+ if %r/#{column_name} #{Regexp.quote(column.sql_type)}(?: COLLATE \w+)? AS \((?<expression>.+?)\) #{column.extra}/ =~ create_table_info
65
+ $~[:expression].inspect
66
+ end
67
+ else
68
+ scope = @connection.send(:quoted_scope, column.table_name)
69
+ column_name = @connection.quote(column.name)
70
+ sql = "SELECT generation_expression FROM information_schema.columns" \
71
+ " WHERE table_schema = #{scope[:schema]}" \
72
+ " AND table_name = #{scope[:name]}" \
73
+ " AND column_name = #{column_name}"
74
+ @connection.query_value(sql, "SCHEMA").inspect
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module MySQL
6
+ module SchemaStatements # :nodoc:
7
+ # Returns an array of indexes for the given table.
8
+ def indexes(table_name)
9
+ indexes = []
10
+ current_index = nil
11
+ execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
12
+ each_hash(result) do |row|
13
+ if current_index != row[:Key_name]
14
+ next if row[:Key_name] == "PRIMARY" # skip the primary key
15
+ current_index = row[:Key_name]
16
+
17
+ mysql_index_type = row[:Index_type].downcase.to_sym
18
+ case mysql_index_type
19
+ when :fulltext, :spatial
20
+ index_type = mysql_index_type
21
+ when :btree, :hash
22
+ index_using = mysql_index_type
23
+ end
24
+
25
+ indexes << [
26
+ row[:Table],
27
+ row[:Key_name],
28
+ row[:Non_unique].to_i == 0,
29
+ [],
30
+ lengths: {},
31
+ orders: {},
32
+ type: index_type,
33
+ using: index_using,
34
+ comment: row[:Index_comment].presence
35
+ ]
36
+ end
37
+
38
+ indexes.last[-2] << row[:Column_name]
39
+ indexes.last[-1][:lengths].merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part]
40
+ indexes.last[-1][:orders].merge!(row[:Column_name] => :desc) if row[:Collation] == "D"
41
+ end
42
+ end
43
+
44
+ indexes.map { |index| IndexDefinition.new(*index) }
45
+ end
46
+
47
+ def remove_column(table_name, column_name, type = nil, options = {})
48
+ if foreign_key_exists?(table_name, column: column_name)
49
+ remove_foreign_key(table_name, column: column_name)
50
+ end
51
+ super
52
+ end
53
+
54
+ def internal_string_options_for_primary_key
55
+ super.tap do |options|
56
+ if CHARSETS_OF_4BYTES_MAXLEN.include?(charset) && (mariadb? || version < "8.0.0")
57
+ options[:collation] = collation.sub(/\A[^_]+/, "utf8")
58
+ end
59
+ end
60
+ end
61
+
62
+ def update_table_definition(table_name, base)
63
+ MySQL::Table.new(table_name, base)
64
+ end
65
+
66
+ def create_schema_dumper(options)
67
+ MySQL::SchemaDumper.create(self, options)
68
+ end
69
+
70
+ private
71
+ CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
72
+
73
+ def schema_creation
74
+ MySQL::SchemaCreation.new(self)
75
+ end
76
+
77
+ def create_table_definition(*args)
78
+ MySQL::TableDefinition.new(*args)
79
+ end
80
+
81
+ def new_column_from_field(table_name, field)
82
+ type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
83
+ if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(field[:Default])
84
+ default, default_function = nil, field[:Default]
85
+ else
86
+ default, default_function = field[:Default], nil
87
+ end
88
+
89
+ MySQL::Column.new(
90
+ field[:Field],
91
+ default,
92
+ type_metadata,
93
+ field[:Null] == "YES",
94
+ table_name,
95
+ default_function,
96
+ field[:Collation],
97
+ comment: field[:Comment].presence
98
+ )
99
+ end
100
+
101
+ def fetch_type_metadata(sql_type, extra = "")
102
+ MySQL::TypeMetadata.new(super(sql_type), extra: extra)
103
+ end
104
+
105
+ def extract_foreign_key_action(specifier)
106
+ super unless specifier == "RESTRICT"
107
+ end
108
+
109
+ def add_index_length(quoted_columns, **options)
110
+ lengths = options_for_index_columns(options[:length])
111
+ quoted_columns.each do |name, column|
112
+ column << "(#{lengths[name]})" if lengths[name].present?
113
+ end
114
+ end
115
+
116
+ def add_options_for_index_columns(quoted_columns, **options)
117
+ quoted_columns = add_index_length(quoted_columns, options)
118
+ super
119
+ end
120
+
121
+ def data_source_sql(name = nil, type: nil)
122
+ scope = quoted_scope(name, type: type)
123
+
124
+ sql = "SELECT table_name FROM information_schema.tables".dup
125
+ sql << " WHERE table_schema = #{scope[:schema]}"
126
+ sql << " AND table_name = #{scope[:name]}" if scope[:name]
127
+ sql << " AND table_type = #{scope[:type]}" if scope[:type]
128
+ sql
129
+ end
130
+
131
+ def quoted_scope(name = nil, type: nil)
132
+ schema, name = extract_schema_qualified_name(name)
133
+ scope = {}
134
+ scope[:schema] = schema ? quote(schema) : "database()"
135
+ scope[:name] = quote(name) if name
136
+ scope[:type] = quote(type) if type
137
+ scope
138
+ end
139
+
140
+ def extract_schema_qualified_name(string)
141
+ schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
142
+ schema, name = nil, schema unless name
143
+ [schema, name]
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end