activerecord 4.2.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.

Files changed (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,133 @@
1
+ require 'active_support/core_ext/big_decimal/conversions'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ module Quoting
6
+ # Quotes the column value to help prevent
7
+ # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
8
+ def quote(value, column = nil)
9
+ # records are quoted as their primary key
10
+ return value.quoted_id if value.respond_to?(:quoted_id)
11
+
12
+ if column
13
+ value = column.cast_type.type_cast_for_database(value)
14
+ end
15
+
16
+ _quote(value)
17
+ end
18
+
19
+ # Cast a +value+ to a type that the database understands. For example,
20
+ # SQLite does not understand dates, so this method will convert a Date
21
+ # to a String.
22
+ def type_cast(value, column)
23
+ if value.respond_to?(:quoted_id) && value.respond_to?(:id)
24
+ return value.id
25
+ end
26
+
27
+ if column
28
+ value = column.cast_type.type_cast_for_database(value)
29
+ end
30
+
31
+ _type_cast(value)
32
+ rescue TypeError
33
+ to_type = column ? " to #{column.type}" : ""
34
+ raise TypeError, "can't cast #{value.class}#{to_type}"
35
+ end
36
+
37
+ # Quotes a string, escaping any ' (single quote) and \ (backslash)
38
+ # characters.
39
+ def quote_string(s)
40
+ s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
41
+ end
42
+
43
+ # Quotes the column name. Defaults to no quoting.
44
+ def quote_column_name(column_name)
45
+ column_name
46
+ end
47
+
48
+ # Quotes the table name. Defaults to column name quoting.
49
+ def quote_table_name(table_name)
50
+ quote_column_name(table_name)
51
+ end
52
+
53
+ # Override to return the quoted table name for assignment. Defaults to
54
+ # table quoting.
55
+ #
56
+ # This works for mysql and mysql2 where table.column can be used to
57
+ # resolve ambiguity.
58
+ #
59
+ # We override this in the sqlite3 and postgresql adapters to use only
60
+ # the column name (as per syntax requirements).
61
+ def quote_table_name_for_assignment(table, attr)
62
+ quote_table_name("#{table}.#{attr}")
63
+ end
64
+
65
+ def quoted_true
66
+ "'t'"
67
+ end
68
+
69
+ def unquoted_true
70
+ 't'
71
+ end
72
+
73
+ def quoted_false
74
+ "'f'"
75
+ end
76
+
77
+ def unquoted_false
78
+ 'f'
79
+ end
80
+
81
+ def quoted_date(value)
82
+ if value.acts_like?(:time)
83
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
84
+
85
+ if value.respond_to?(zone_conversion_method)
86
+ value = value.send(zone_conversion_method)
87
+ end
88
+ end
89
+
90
+ value.to_s(:db)
91
+ end
92
+
93
+ private
94
+
95
+ def types_which_need_no_typecasting
96
+ [nil, Numeric, String]
97
+ end
98
+
99
+ def _quote(value)
100
+ case value
101
+ when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data
102
+ "'#{quote_string(value.to_s)}'"
103
+ when true then quoted_true
104
+ when false then quoted_false
105
+ when nil then "NULL"
106
+ # BigDecimals need to be put in a non-normalized form and quoted.
107
+ when BigDecimal then value.to_s('F')
108
+ when Numeric, ActiveSupport::Duration then value.to_s
109
+ when Date, Time then "'#{quoted_date(value)}'"
110
+ when Symbol then "'#{quote_string(value.to_s)}'"
111
+ when Class then "'#{value}'"
112
+ else
113
+ "'#{quote_string(YAML.dump(value))}'"
114
+ end
115
+ end
116
+
117
+ def _type_cast(value)
118
+ case value
119
+ when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
120
+ value.to_s
121
+ when true then unquoted_true
122
+ when false then unquoted_false
123
+ # BigDecimals need to be put in a non-normalized form and quoted.
124
+ when BigDecimal then value.to_s('F')
125
+ when Date, Time then quoted_date(value)
126
+ when *types_which_need_no_typecasting
127
+ value
128
+ else raise TypeError
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Savepoints #:nodoc:
4
+ def supports_savepoints?
5
+ true
6
+ end
7
+
8
+ def create_savepoint(name = current_savepoint_name)
9
+ execute("SAVEPOINT #{name}")
10
+ end
11
+
12
+ def rollback_to_savepoint(name = current_savepoint_name)
13
+ execute("ROLLBACK TO SAVEPOINT #{name}")
14
+ end
15
+
16
+ def release_savepoint(name = current_savepoint_name)
17
+ execute("RELEASE SAVEPOINT #{name}")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,125 @@
1
+ require 'active_support/core_ext/string/strip'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class AbstractAdapter
6
+ class SchemaCreation # :nodoc:
7
+ def initialize(conn)
8
+ @conn = conn
9
+ @cache = {}
10
+ end
11
+
12
+ def accept(o)
13
+ m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
14
+ send m, o
15
+ end
16
+
17
+ def visit_AddColumn(o)
18
+ "ADD #{accept(o)}"
19
+ end
20
+
21
+ private
22
+
23
+ def visit_AlterTable(o)
24
+ sql = "ALTER TABLE #{quote_table_name(o.name)} "
25
+ sql << o.adds.map { |col| visit_AddColumn col }.join(' ')
26
+ sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(' ')
27
+ sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(' ')
28
+ end
29
+
30
+ def visit_ColumnDefinition(o)
31
+ sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
32
+ column_sql = "#{quote_column_name(o.name)} #{sql_type}"
33
+ add_column_options!(column_sql, column_options(o)) unless o.primary_key?
34
+ column_sql
35
+ end
36
+
37
+ def visit_TableDefinition(o)
38
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
39
+ create_sql << "#{quote_table_name(o.name)} "
40
+ create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " unless o.as
41
+ create_sql << "#{o.options}"
42
+ create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
43
+ create_sql
44
+ end
45
+
46
+ def visit_AddForeignKey(o)
47
+ sql = <<-SQL.strip_heredoc
48
+ ADD CONSTRAINT #{quote_column_name(o.name)}
49
+ FOREIGN KEY (#{quote_column_name(o.column)})
50
+ REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
51
+ SQL
52
+ sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
53
+ sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
54
+ sql
55
+ end
56
+
57
+ def visit_DropForeignKey(name)
58
+ "DROP CONSTRAINT #{quote_column_name(name)}"
59
+ end
60
+
61
+ def column_options(o)
62
+ column_options = {}
63
+ column_options[:null] = o.null unless o.null.nil?
64
+ column_options[:default] = o.default unless o.default.nil?
65
+ column_options[:column] = o
66
+ column_options[:first] = o.first
67
+ column_options[:after] = o.after
68
+ column_options
69
+ end
70
+
71
+ def quote_column_name(name)
72
+ @conn.quote_column_name name
73
+ end
74
+
75
+ def quote_table_name(name)
76
+ @conn.quote_table_name name
77
+ end
78
+
79
+ def type_to_sql(type, limit, precision, scale)
80
+ @conn.type_to_sql type.to_sym, limit, precision, scale
81
+ end
82
+
83
+ def add_column_options!(sql, options)
84
+ sql << " DEFAULT #{quote_value(options[:default], options[:column])}" if options_include_default?(options)
85
+ # must explicitly check for :null to allow change_column to work on migrations
86
+ if options[:null] == false
87
+ sql << " NOT NULL"
88
+ end
89
+ if options[:auto_increment] == true
90
+ sql << " AUTO_INCREMENT"
91
+ end
92
+ sql
93
+ end
94
+
95
+ def quote_value(value, column)
96
+ column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
97
+ column.cast_type ||= type_for_column(column)
98
+
99
+ @conn.quote(value, column)
100
+ end
101
+
102
+ def options_include_default?(options)
103
+ options.include?(:default) && !(options[:null] == false && options[:default].nil?)
104
+ end
105
+
106
+ def action_sql(action, dependency)
107
+ case dependency
108
+ when :nullify then "ON #{action} SET NULL"
109
+ when :cascade then "ON #{action} CASCADE"
110
+ when :restrict then "ON #{action} RESTRICT"
111
+ else
112
+ raise ArgumentError, <<-MSG.strip_heredoc
113
+ '#{dependency}' is not supported for :on_update or :on_delete.
114
+ Supported values are: :nullify, :cascade, :restrict
115
+ MSG
116
+ end
117
+ end
118
+
119
+ def type_for_column(column)
120
+ @conn.lookup_cast_type(column.sql_type)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,574 @@
1
+ require 'date'
2
+ require 'set'
3
+ require 'bigdecimal'
4
+ require 'bigdecimal/util'
5
+
6
+ module ActiveRecord
7
+ module ConnectionAdapters #:nodoc:
8
+ # Abstract representation of an index definition on a table. Instances of
9
+ # this type are typically created and returned by methods in database
10
+ # adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
11
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using) #:nodoc:
12
+ end
13
+
14
+ # Abstract representation of a column definition. Instances of this type
15
+ # are typically created by methods in TableDefinition, and added to the
16
+ # +columns+ attribute of said TableDefinition object, in order to be used
17
+ # for generating a number of table creation or table changing SQL statements.
18
+ class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key, :sql_type, :cast_type) #:nodoc:
19
+
20
+ def primary_key?
21
+ primary_key || type.to_sym == :primary_key
22
+ end
23
+ end
24
+
25
+ class ChangeColumnDefinition < Struct.new(:column, :type, :options) #:nodoc:
26
+ end
27
+
28
+ class ForeignKeyDefinition < Struct.new(:from_table, :to_table, :options) #:nodoc:
29
+ def name
30
+ options[:name]
31
+ end
32
+
33
+ def column
34
+ options[:column]
35
+ end
36
+
37
+ def primary_key
38
+ options[:primary_key] || default_primary_key
39
+ end
40
+
41
+ def on_delete
42
+ options[:on_delete]
43
+ end
44
+
45
+ def on_update
46
+ options[:on_update]
47
+ end
48
+
49
+ def custom_primary_key?
50
+ options[:primary_key] != default_primary_key
51
+ end
52
+
53
+ private
54
+ def default_primary_key
55
+ "id"
56
+ end
57
+ end
58
+
59
+ module TimestampDefaultDeprecation # :nodoc:
60
+ def emit_warning_if_null_unspecified(options)
61
+ return if options.key?(:null)
62
+
63
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
64
+ `#timestamp` was called without specifying an option for `null`. In Rails 5,
65
+ this behavior will change to `null: false`. You should manually specify
66
+ `null: true` to prevent the behavior of your existing migrations from changing.
67
+ MSG
68
+ end
69
+ end
70
+
71
+ # Represents the schema of an SQL table in an abstract way. This class
72
+ # provides methods for manipulating the schema representation.
73
+ #
74
+ # Inside migration files, the +t+ object in +create_table+
75
+ # is actually of this type:
76
+ #
77
+ # class SomeMigration < ActiveRecord::Migration
78
+ # def up
79
+ # create_table :foo do |t|
80
+ # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
81
+ # end
82
+ # end
83
+ #
84
+ # def down
85
+ # ...
86
+ # end
87
+ # end
88
+ #
89
+ # The table definitions
90
+ # The Columns are stored as a ColumnDefinition in the +columns+ attribute.
91
+ class TableDefinition
92
+ include TimestampDefaultDeprecation
93
+
94
+ # An array of ColumnDefinition objects, representing the column changes
95
+ # that have been defined.
96
+ attr_accessor :indexes
97
+ attr_reader :name, :temporary, :options, :as
98
+
99
+ def initialize(types, name, temporary, options, as = nil)
100
+ @columns_hash = {}
101
+ @indexes = {}
102
+ @native = types
103
+ @temporary = temporary
104
+ @options = options
105
+ @as = as
106
+ @name = name
107
+ end
108
+
109
+ def columns; @columns_hash.values; end
110
+
111
+ # Appends a primary key definition to the table definition.
112
+ # Can be called multiple times, but this is probably not a good idea.
113
+ def primary_key(name, type = :primary_key, options = {})
114
+ column(name, type, options.merge(:primary_key => true))
115
+ end
116
+
117
+ # Returns a ColumnDefinition for the column with name +name+.
118
+ def [](name)
119
+ @columns_hash[name.to_s]
120
+ end
121
+
122
+ # Instantiates a new column for the table.
123
+ # The +type+ parameter is normally one of the migrations native types,
124
+ # which is one of the following:
125
+ # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
126
+ # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
127
+ # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
128
+ # <tt>:binary</tt>, <tt>:boolean</tt>.
129
+ #
130
+ # You may use a type not in this list as long as it is supported by your
131
+ # database (for example, "polygon" in MySQL), but this will not be database
132
+ # agnostic and should usually be avoided.
133
+ #
134
+ # Available options are (none of these exists by default):
135
+ # * <tt>:limit</tt> -
136
+ # Requests a maximum column length. This is number of characters for <tt>:string</tt> and
137
+ # <tt>:text</tt> columns and number of bytes for <tt>:binary</tt> and <tt>:integer</tt> columns.
138
+ # * <tt>:default</tt> -
139
+ # The column's default value. Use nil for NULL.
140
+ # * <tt>:null</tt> -
141
+ # Allows or disallows +NULL+ values in the column. This option could
142
+ # have been named <tt>:null_allowed</tt>.
143
+ # * <tt>:precision</tt> -
144
+ # Specifies the precision for a <tt>:decimal</tt> column.
145
+ # * <tt>:scale</tt> -
146
+ # Specifies the scale for a <tt>:decimal</tt> column.
147
+ # * <tt>:index</tt> -
148
+ # Create an index for the column. Can be either <tt>true</tt> or an options hash.
149
+ #
150
+ # Note: The precision is the total number of significant digits
151
+ # and the scale is the number of digits that can be stored following
152
+ # the decimal point. For example, the number 123.45 has a precision of 5
153
+ # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
154
+ # range from -999.99 to 999.99.
155
+ #
156
+ # Please be aware of different RDBMS implementations behavior with
157
+ # <tt>:decimal</tt> columns:
158
+ # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
159
+ # <tt>:precision</tt>, and makes no comments about the requirements of
160
+ # <tt>:precision</tt>.
161
+ # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
162
+ # Default is (10,0).
163
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
164
+ # <tt>:scale</tt> [0..infinity]. No default.
165
+ # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
166
+ # Internal storage as strings. No default.
167
+ # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
168
+ # but the maximum supported <tt>:precision</tt> is 16. No default.
169
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
170
+ # Default is (38,0).
171
+ # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
172
+ # Default unknown.
173
+ # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
174
+ # Default (38,0).
175
+ #
176
+ # This method returns <tt>self</tt>.
177
+ #
178
+ # == Examples
179
+ # # Assuming +td+ is an instance of TableDefinition
180
+ # td.column(:granted, :boolean)
181
+ # # granted BOOLEAN
182
+ #
183
+ # td.column(:picture, :binary, limit: 2.megabytes)
184
+ # # => picture BLOB(2097152)
185
+ #
186
+ # td.column(:sales_stage, :string, limit: 20, default: 'new', null: false)
187
+ # # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
188
+ #
189
+ # td.column(:bill_gates_money, :decimal, precision: 15, scale: 2)
190
+ # # => bill_gates_money DECIMAL(15,2)
191
+ #
192
+ # td.column(:sensor_reading, :decimal, precision: 30, scale: 20)
193
+ # # => sensor_reading DECIMAL(30,20)
194
+ #
195
+ # # While <tt>:scale</tt> defaults to zero on most databases, it
196
+ # # probably wouldn't hurt to include it.
197
+ # td.column(:huge_integer, :decimal, precision: 30)
198
+ # # => huge_integer DECIMAL(30)
199
+ #
200
+ # # Defines a column with a database-specific type.
201
+ # td.column(:foo, 'polygon')
202
+ # # => foo polygon
203
+ #
204
+ # == Short-hand examples
205
+ #
206
+ # Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
207
+ # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
208
+ # in a single statement.
209
+ #
210
+ # What can be written like this with the regular calls to column:
211
+ #
212
+ # create_table :products do |t|
213
+ # t.column :shop_id, :integer
214
+ # t.column :creator_id, :integer
215
+ # t.column :item_number, :string
216
+ # t.column :name, :string, default: "Untitled"
217
+ # t.column :value, :string, default: "Untitled"
218
+ # t.column :created_at, :datetime
219
+ # t.column :updated_at, :datetime
220
+ # end
221
+ # add_index :products, :item_number
222
+ #
223
+ # can also be written as follows using the short-hand:
224
+ #
225
+ # create_table :products do |t|
226
+ # t.integer :shop_id, :creator_id
227
+ # t.string :item_number, index: true
228
+ # t.string :name, :value, default: "Untitled"
229
+ # t.timestamps null: false
230
+ # end
231
+ #
232
+ # There's a short-hand method for each of the type values declared at the top. And then there's
233
+ # TableDefinition#timestamps that'll add +created_at+ and +updated_at+ as datetimes.
234
+ #
235
+ # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
236
+ # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
237
+ # options, these will be used when creating the <tt>_type</tt> column. The <tt>:index</tt> option
238
+ # will also create an index, similar to calling <tt>add_index</tt>. So what can be written like this:
239
+ #
240
+ # create_table :taggings do |t|
241
+ # t.integer :tag_id, :tagger_id, :taggable_id
242
+ # t.string :tagger_type
243
+ # t.string :taggable_type, default: 'Photo'
244
+ # end
245
+ # add_index :taggings, :tag_id, name: 'index_taggings_on_tag_id'
246
+ # add_index :taggings, [:tagger_id, :tagger_type]
247
+ #
248
+ # Can also be written as follows using references:
249
+ #
250
+ # create_table :taggings do |t|
251
+ # t.references :tag, index: { name: 'index_taggings_on_tag_id' }
252
+ # t.references :tagger, polymorphic: true, index: true
253
+ # t.references :taggable, polymorphic: { default: 'Photo' }
254
+ # end
255
+ def column(name, type, options = {})
256
+ name = name.to_s
257
+ type = type.to_sym
258
+
259
+ if @columns_hash[name] && @columns_hash[name].primary_key?
260
+ raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
261
+ end
262
+
263
+ index_options = options.delete(:index)
264
+ index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
265
+ @columns_hash[name] = new_column_definition(name, type, options)
266
+ self
267
+ end
268
+
269
+ def remove_column(name)
270
+ @columns_hash.delete name.to_s
271
+ end
272
+
273
+ [:string, :text, :integer, :bigint, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
274
+ define_method column_type do |*args|
275
+ options = args.extract_options!
276
+ column_names = args
277
+ column_names.each { |name| column(name, column_type, options) }
278
+ end
279
+ end
280
+
281
+ # Adds index options to the indexes hash, keyed by column name
282
+ # This is primarily used to track indexes that need to be created after the table
283
+ #
284
+ # index(:account_id, name: 'index_projects_on_account_id')
285
+ def index(column_name, options = {})
286
+ indexes[column_name] = options
287
+ end
288
+
289
+ # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
290
+ # <tt>:updated_at</tt> to the table. See SchemaStatements#add_timestamps
291
+ #
292
+ # t.timestamps null: false
293
+ def timestamps(*args)
294
+ options = args.extract_options!
295
+ emit_warning_if_null_unspecified(options)
296
+ column(:created_at, :datetime, options)
297
+ column(:updated_at, :datetime, options)
298
+ end
299
+
300
+ # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
301
+ # <tt>references</tt> and <tt>belongs_to</tt> are acceptable. The reference column will be an +integer+
302
+ # by default, the <tt>:type</tt> option can be used to specify a different type.
303
+ #
304
+ # t.references(:user)
305
+ # t.references(:user, type: "string")
306
+ # t.belongs_to(:supplier, polymorphic: true)
307
+ #
308
+ # See SchemaStatements#add_reference
309
+ def references(*args)
310
+ options = args.extract_options!
311
+ polymorphic = options.delete(:polymorphic)
312
+ index_options = options.delete(:index)
313
+ type = options.delete(:type) || :integer
314
+ args.each do |col|
315
+ column("#{col}_id", type, options)
316
+ column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
317
+ index(polymorphic ? %w(type id).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
318
+ end
319
+ end
320
+ alias :belongs_to :references
321
+
322
+ def new_column_definition(name, type, options) # :nodoc:
323
+ type = aliased_types(type.to_s, type)
324
+ column = create_column_definition name, type
325
+ limit = options.fetch(:limit) do
326
+ native[type][:limit] if native[type].is_a?(Hash)
327
+ end
328
+
329
+ column.limit = limit
330
+ column.precision = options[:precision]
331
+ column.scale = options[:scale]
332
+ column.default = options[:default]
333
+ column.null = options[:null]
334
+ column.first = options[:first]
335
+ column.after = options[:after]
336
+ column.primary_key = type == :primary_key || options[:primary_key]
337
+ column
338
+ end
339
+
340
+ private
341
+ def create_column_definition(name, type)
342
+ ColumnDefinition.new name, type
343
+ end
344
+
345
+ def native
346
+ @native
347
+ end
348
+
349
+ def aliased_types(name, fallback)
350
+ 'timestamp' == name ? :datetime : fallback
351
+ end
352
+ end
353
+
354
+ class AlterTable # :nodoc:
355
+ attr_reader :adds
356
+ attr_reader :foreign_key_adds
357
+ attr_reader :foreign_key_drops
358
+
359
+ def initialize(td)
360
+ @td = td
361
+ @adds = []
362
+ @foreign_key_adds = []
363
+ @foreign_key_drops = []
364
+ end
365
+
366
+ def name; @td.name; end
367
+
368
+ def add_foreign_key(to_table, options)
369
+ @foreign_key_adds << ForeignKeyDefinition.new(name, to_table, options)
370
+ end
371
+
372
+ def drop_foreign_key(name)
373
+ @foreign_key_drops << name
374
+ end
375
+
376
+ def add_column(name, type, options)
377
+ name = name.to_s
378
+ type = type.to_sym
379
+ @adds << @td.new_column_definition(name, type, options)
380
+ end
381
+ end
382
+
383
+ # Represents an SQL table in an abstract way for updating a table.
384
+ # Also see TableDefinition and SchemaStatements#create_table
385
+ #
386
+ # Available transformations are:
387
+ #
388
+ # change_table :table do |t|
389
+ # t.column
390
+ # t.index
391
+ # t.rename_index
392
+ # t.timestamps
393
+ # t.change
394
+ # t.change_default
395
+ # t.rename
396
+ # t.references
397
+ # t.belongs_to
398
+ # t.string
399
+ # t.text
400
+ # t.integer
401
+ # t.float
402
+ # t.decimal
403
+ # t.datetime
404
+ # t.timestamp
405
+ # t.time
406
+ # t.date
407
+ # t.binary
408
+ # t.boolean
409
+ # t.remove
410
+ # t.remove_references
411
+ # t.remove_belongs_to
412
+ # t.remove_index
413
+ # t.remove_timestamps
414
+ # end
415
+ #
416
+ class Table
417
+ attr_reader :name
418
+
419
+ def initialize(table_name, base)
420
+ @name = table_name
421
+ @base = base
422
+ end
423
+
424
+ # Adds a new column to the named table.
425
+ # See TableDefinition#column for details of the options you can use.
426
+ #
427
+ # ====== Creating a simple column
428
+ # t.column(:name, :string)
429
+ def column(column_name, type, options = {})
430
+ @base.add_column(name, column_name, type, options)
431
+ end
432
+
433
+ # Checks to see if a column exists. See SchemaStatements#column_exists?
434
+ def column_exists?(column_name, type = nil, options = {})
435
+ @base.column_exists?(name, column_name, type, options)
436
+ end
437
+
438
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
439
+ # an Array of Symbols. See SchemaStatements#add_index
440
+ #
441
+ # ====== Creating a simple index
442
+ # t.index(:name)
443
+ # ====== Creating a unique index
444
+ # t.index([:branch_id, :party_id], unique: true)
445
+ # ====== Creating a named index
446
+ # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
447
+ def index(column_name, options = {})
448
+ @base.add_index(name, column_name, options)
449
+ end
450
+
451
+ # Checks to see if an index exists. See SchemaStatements#index_exists?
452
+ def index_exists?(column_name, options = {})
453
+ @base.index_exists?(name, column_name, options)
454
+ end
455
+
456
+ # Renames the given index on the table.
457
+ #
458
+ # t.rename_index(:user_id, :account_id)
459
+ def rename_index(index_name, new_index_name)
460
+ @base.rename_index(name, index_name, new_index_name)
461
+ end
462
+
463
+ # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
464
+ #
465
+ # t.timestamps null: false
466
+ def timestamps(options = {})
467
+ @base.add_timestamps(name, options)
468
+ end
469
+
470
+ # Changes the column's definition according to the new options.
471
+ # See TableDefinition#column for details of the options you can use.
472
+ #
473
+ # t.change(:name, :string, limit: 80)
474
+ # t.change(:description, :text)
475
+ def change(column_name, type, options = {})
476
+ @base.change_column(name, column_name, type, options)
477
+ end
478
+
479
+ # Sets a new default value for a column. See SchemaStatements#change_column_default
480
+ #
481
+ # t.change_default(:qualification, 'new')
482
+ # t.change_default(:authorized, 1)
483
+ def change_default(column_name, default)
484
+ @base.change_column_default(name, column_name, default)
485
+ end
486
+
487
+ # Removes the column(s) from the table definition.
488
+ #
489
+ # t.remove(:qualification)
490
+ # t.remove(:qualification, :experience)
491
+ def remove(*column_names)
492
+ @base.remove_columns(name, *column_names)
493
+ end
494
+
495
+ # Removes the given index from the table.
496
+ #
497
+ # ====== Remove the index_table_name_on_column in the table_name table
498
+ # t.remove_index :column
499
+ # ====== Remove the index named index_table_name_on_branch_id in the table_name table
500
+ # t.remove_index column: :branch_id
501
+ # ====== Remove the index named index_table_name_on_branch_id_and_party_id in the table_name table
502
+ # t.remove_index column: [:branch_id, :party_id]
503
+ # ====== Remove the index named by_branch_party in the table_name table
504
+ # t.remove_index name: :by_branch_party
505
+ def remove_index(options = {})
506
+ @base.remove_index(name, options)
507
+ end
508
+
509
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
510
+ #
511
+ # t.remove_timestamps
512
+ def remove_timestamps(options = {})
513
+ @base.remove_timestamps(name, options)
514
+ end
515
+
516
+ # Renames a column.
517
+ #
518
+ # t.rename(:description, :name)
519
+ def rename(column_name, new_column_name)
520
+ @base.rename_column(name, column_name, new_column_name)
521
+ end
522
+
523
+ # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
524
+ # <tt>references</tt> and <tt>belongs_to</tt> are acceptable. The reference column will be an +integer+
525
+ # by default, the <tt>:type</tt> option can be used to specify a different type.
526
+ #
527
+ # t.references(:user)
528
+ # t.references(:user, type: "string")
529
+ # t.belongs_to(:supplier, polymorphic: true)
530
+ #
531
+ # See SchemaStatements#add_reference
532
+ def references(*args)
533
+ options = args.extract_options!
534
+ args.each do |ref_name|
535
+ @base.add_reference(name, ref_name, options)
536
+ end
537
+ end
538
+ alias :belongs_to :references
539
+
540
+ # Removes a reference. Optionally removes a +type+ column.
541
+ # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
542
+ #
543
+ # t.remove_references(:user)
544
+ # t.remove_belongs_to(:supplier, polymorphic: true)
545
+ #
546
+ # See SchemaStatements#remove_reference
547
+ def remove_references(*args)
548
+ options = args.extract_options!
549
+ args.each do |ref_name|
550
+ @base.remove_reference(name, ref_name, options)
551
+ end
552
+ end
553
+ alias :remove_belongs_to :remove_references
554
+
555
+ # Adds a column or columns of a specified type
556
+ #
557
+ # t.string(:goat)
558
+ # t.string(:goat, :sheep)
559
+ [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
560
+ define_method column_type do |*args|
561
+ options = args.extract_options!
562
+ args.each do |column_name|
563
+ @base.add_column(name, column_name, column_type, options)
564
+ end
565
+ end
566
+ end
567
+
568
+ private
569
+ def native
570
+ @base.native_database_types
571
+ end
572
+ end
573
+ end
574
+ end