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,75 @@
1
+ require 'active_support/core_ext/hash/except'
2
+ require 'active_support/core_ext/hash/slice'
3
+ require 'active_record/relation/merger'
4
+
5
+ module ActiveRecord
6
+ module SpawnMethods
7
+
8
+ # This is overridden by Associations::CollectionProxy
9
+ def spawn #:nodoc:
10
+ clone
11
+ end
12
+
13
+ # Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an <tt>ActiveRecord::Relation</tt>.
14
+ # Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
15
+ # Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
16
+ # # Performs a single join query with both where conditions.
17
+ #
18
+ # recent_posts = Post.order('created_at DESC').first(5)
19
+ # Post.where(published: true).merge(recent_posts)
20
+ # # Returns the intersection of all published posts with the 5 most recently created posts.
21
+ # # (This is just an example. You'd probably want to do this with a single query!)
22
+ #
23
+ # Procs will be evaluated by merge:
24
+ #
25
+ # Post.where(published: true).merge(-> { joins(:comments) })
26
+ # # => Post.where(published: true).joins(:comments)
27
+ #
28
+ # This is mainly intended for sharing common conditions between multiple associations.
29
+ def merge(other)
30
+ if other.is_a?(Array)
31
+ to_a & other
32
+ elsif other
33
+ spawn.merge!(other)
34
+ else
35
+ self
36
+ end
37
+ end
38
+
39
+ def merge!(other) # :nodoc:
40
+ if !other.is_a?(Relation) && other.respond_to?(:to_proc)
41
+ instance_exec(&other)
42
+ else
43
+ klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
44
+ klass.new(self, other).merge
45
+ end
46
+ end
47
+
48
+ # Removes from the query the condition(s) specified in +skips+.
49
+ #
50
+ # Post.order('id asc').except(:order) # discards the order condition
51
+ # Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order
52
+ def except(*skips)
53
+ relation_with values.except(*skips)
54
+ end
55
+
56
+ # Removes any condition from the query other than the one(s) specified in +onlies+.
57
+ #
58
+ # Post.order('id asc').only(:where) # discards the order condition
59
+ # Post.order('id asc').only(:where, :order) # uses the specified order
60
+ def only(*onlies)
61
+ if onlies.any? { |o| o == :where }
62
+ onlies << :bind
63
+ end
64
+ relation_with values.slice(*onlies)
65
+ end
66
+
67
+ private
68
+
69
+ def relation_with(values) # :nodoc:
70
+ result = Relation.create(klass, table, values)
71
+ result.extend(*extending_values) if extending_values.any?
72
+ result
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,131 @@
1
+ module ActiveRecord
2
+ ###
3
+ # This class encapsulates a Result returned from calling +exec_query+ on any
4
+ # database connection adapter. For example:
5
+ #
6
+ # result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
7
+ # result # => #<ActiveRecord::Result:0xdeadbeef>
8
+ #
9
+ # # Get the column names of the result:
10
+ # result.columns
11
+ # # => ["id", "title", "body"]
12
+ #
13
+ # # Get the record values of the result:
14
+ # result.rows
15
+ # # => [[1, "title_1", "body_1"],
16
+ # [2, "title_2", "body_2"],
17
+ # ...
18
+ # ]
19
+ #
20
+ # # Get an array of hashes representing the result (column => value):
21
+ # result.to_hash
22
+ # # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
23
+ # {"id" => 2, "title" => "title_2", "body" => "body_2"},
24
+ # ...
25
+ # ]
26
+ #
27
+ # # ActiveRecord::Result also includes Enumerable.
28
+ # result.each do |row|
29
+ # puts row['title'] + " " + row['body']
30
+ # end
31
+ class Result
32
+ include Enumerable
33
+
34
+ IDENTITY_TYPE = Type::Value.new # :nodoc:
35
+
36
+ attr_reader :columns, :rows, :column_types
37
+
38
+ def initialize(columns, rows, column_types = {})
39
+ @columns = columns
40
+ @rows = rows
41
+ @hash_rows = nil
42
+ @column_types = column_types
43
+ end
44
+
45
+ def length
46
+ @rows.length
47
+ end
48
+
49
+ def each
50
+ if block_given?
51
+ hash_rows.each { |row| yield row }
52
+ else
53
+ hash_rows.to_enum { @rows.size }
54
+ end
55
+ end
56
+
57
+ def to_hash
58
+ hash_rows
59
+ end
60
+
61
+ alias :map! :map
62
+ alias :collect! :map
63
+
64
+ # Returns true if there are no records.
65
+ def empty?
66
+ rows.empty?
67
+ end
68
+
69
+ def to_ary
70
+ hash_rows
71
+ end
72
+
73
+ def [](idx)
74
+ hash_rows[idx]
75
+ end
76
+
77
+ def last
78
+ hash_rows.last
79
+ end
80
+
81
+ def cast_values(type_overrides = {}) # :nodoc:
82
+ types = columns.map { |name| column_type(name, type_overrides) }
83
+ result = rows.map do |values|
84
+ types.zip(values).map { |type, value| type.type_cast_from_database(value) }
85
+ end
86
+
87
+ columns.one? ? result.map!(&:first) : result
88
+ end
89
+
90
+ def initialize_copy(other)
91
+ @columns = columns.dup
92
+ @rows = rows.dup
93
+ @column_types = column_types.dup
94
+ @hash_rows = nil
95
+ end
96
+
97
+ private
98
+
99
+ def column_type(name, type_overrides = {})
100
+ type_overrides.fetch(name) do
101
+ column_types.fetch(name, IDENTITY_TYPE)
102
+ end
103
+ end
104
+
105
+ def hash_rows
106
+ @hash_rows ||=
107
+ begin
108
+ # We freeze the strings to prevent them getting duped when
109
+ # used as keys in ActiveRecord::Base's @attributes hash
110
+ columns = @columns.map { |c| c.dup.freeze }
111
+ @rows.map { |row|
112
+ # In the past we used Hash[columns.zip(row)]
113
+ # though elegant, the verbose way is much more efficient
114
+ # both time and memory wise cause it avoids a big array allocation
115
+ # this method is called a lot and needs to be micro optimised
116
+ hash = {}
117
+
118
+ index = 0
119
+ length = columns.length
120
+
121
+ while index < length
122
+ hash[columns[index]] = row[index]
123
+ index += 1
124
+ end
125
+
126
+ hash
127
+ }
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,22 @@
1
+ require 'active_support/per_thread_registry'
2
+
3
+ module ActiveRecord
4
+ # This is a thread locals registry for Active Record. For example:
5
+ #
6
+ # ActiveRecord::RuntimeRegistry.connection_handler
7
+ #
8
+ # returns the connection handler local to the current thread.
9
+ #
10
+ # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
11
+ # for further details.
12
+ class RuntimeRegistry # :nodoc:
13
+ extend ActiveSupport::PerThreadRegistry
14
+
15
+ attr_accessor :connection_handler, :sql_runtime, :connection_id
16
+
17
+ [:connection_handler, :sql_runtime, :connection_id].each do |val|
18
+ class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
19
+ class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,191 @@
1
+ module ActiveRecord
2
+ module Sanitization
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def quote_value(value, column) #:nodoc:
7
+ connection.quote(value, column)
8
+ end
9
+
10
+ # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
11
+ def sanitize(object) #:nodoc:
12
+ connection.quote(object)
13
+ end
14
+
15
+ protected
16
+
17
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
18
+ # them into a valid SQL fragment for a WHERE clause.
19
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
20
+ # { name: "foo'bar", group_id: 4 } returns "name='foo''bar' and group_id='4'"
21
+ # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
22
+ def sanitize_sql_for_conditions(condition, table_name = self.table_name)
23
+ return nil if condition.blank?
24
+
25
+ case condition
26
+ when Array; sanitize_sql_array(condition)
27
+ when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
28
+ else condition
29
+ end
30
+ end
31
+ alias_method :sanitize_sql, :sanitize_sql_for_conditions
32
+ alias_method :sanitize_conditions, :sanitize_sql
33
+
34
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
35
+ # them into a valid SQL fragment for a SET clause.
36
+ # { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
37
+ def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
38
+ case assignments
39
+ when Array; sanitize_sql_array(assignments)
40
+ when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
41
+ else assignments
42
+ end
43
+ end
44
+
45
+ # Accepts a hash of SQL conditions and replaces those attributes
46
+ # that correspond to a +composed_of+ relationship with their expanded
47
+ # aggregate attribute values.
48
+ # Given:
49
+ # class Person < ActiveRecord::Base
50
+ # composed_of :address, class_name: "Address",
51
+ # mapping: [%w(address_street street), %w(address_city city)]
52
+ # end
53
+ # Then:
54
+ # { address: Address.new("813 abc st.", "chicago") }
55
+ # # => { address_street: "813 abc st.", address_city: "chicago" }
56
+ def expand_hash_conditions_for_aggregates(attrs)
57
+ expanded_attrs = {}
58
+ attrs.each do |attr, value|
59
+ if aggregation = reflect_on_aggregation(attr.to_sym)
60
+ mapping = aggregation.mapping
61
+ mapping.each do |field_attr, aggregate_attr|
62
+ if mapping.size == 1 && !value.respond_to?(aggregate_attr)
63
+ expanded_attrs[field_attr] = value
64
+ else
65
+ expanded_attrs[field_attr] = value.send(aggregate_attr)
66
+ end
67
+ end
68
+ else
69
+ expanded_attrs[attr] = value
70
+ end
71
+ end
72
+ expanded_attrs
73
+ end
74
+
75
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
76
+ # { name: "foo'bar", group_id: 4 }
77
+ # # => "name='foo''bar' and group_id= 4"
78
+ # { status: nil, group_id: [1,2,3] }
79
+ # # => "status IS NULL and group_id IN (1,2,3)"
80
+ # { age: 13..18 }
81
+ # # => "age BETWEEN 13 AND 18"
82
+ # { 'other_records.id' => 7 }
83
+ # # => "`other_records`.`id` = 7"
84
+ # { other_records: { id: 7 } }
85
+ # # => "`other_records`.`id` = 7"
86
+ # And for value objects on a composed_of relationship:
87
+ # { address: Address.new("123 abc st.", "chicago") }
88
+ # # => "address_street='123 abc st.' and address_city='chicago'"
89
+ def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
90
+ ActiveSupport::Deprecation.warn(<<-EOWARN)
91
+ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
92
+ EOWARN
93
+ attrs = PredicateBuilder.resolve_column_aliases self, attrs
94
+ attrs = expand_hash_conditions_for_aggregates(attrs)
95
+
96
+ table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
97
+ PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
98
+ connection.visitor.compile b
99
+ }.join(' AND ')
100
+ end
101
+ alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
102
+
103
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
104
+ # { status: nil, group_id: 1 }
105
+ # # => "status = NULL , group_id = 1"
106
+ def sanitize_sql_hash_for_assignment(attrs, table)
107
+ c = connection
108
+ attrs.map do |attr, value|
109
+ "#{c.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, c, columns_hash[attr.to_s])}"
110
+ end.join(', ')
111
+ end
112
+
113
+ # Sanitizes a +string+ so that it is safe to use within an SQL
114
+ # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%"
115
+ def sanitize_sql_like(string, escape_character = "\\")
116
+ pattern = Regexp.union(escape_character, "%", "_")
117
+ string.gsub(pattern) { |x| [escape_character, x].join }
118
+ end
119
+
120
+ # Accepts an array of conditions. The array has each value
121
+ # sanitized and interpolated into the SQL statement.
122
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
123
+ def sanitize_sql_array(ary)
124
+ statement, *values = ary
125
+ if values.first.is_a?(Hash) && statement =~ /:\w+/
126
+ replace_named_bind_variables(statement, values.first)
127
+ elsif statement.include?('?')
128
+ replace_bind_variables(statement, values)
129
+ elsif statement.blank?
130
+ statement
131
+ else
132
+ statement % values.collect { |value| connection.quote_string(value.to_s) }
133
+ end
134
+ end
135
+
136
+ def replace_bind_variables(statement, values) #:nodoc:
137
+ raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
138
+ bound = values.dup
139
+ c = connection
140
+ statement.gsub(/\?/) do
141
+ replace_bind_variable(bound.shift, c)
142
+ end
143
+ end
144
+
145
+ def replace_bind_variable(value, c = connection) #:nodoc:
146
+ if ActiveRecord::Relation === value
147
+ value.to_sql
148
+ else
149
+ quote_bound_value(value, c)
150
+ end
151
+ end
152
+
153
+ def replace_named_bind_variables(statement, bind_vars) #:nodoc:
154
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do
155
+ if $1 == ':' # skip postgresql casts
156
+ $& # return the whole match
157
+ elsif bind_vars.include?(match = $2.to_sym)
158
+ replace_bind_variable(bind_vars[match])
159
+ else
160
+ raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
161
+ end
162
+ end
163
+ end
164
+
165
+ def quote_bound_value(value, c = connection, column = nil) #:nodoc:
166
+ if column
167
+ c.quote(value, column)
168
+ elsif value.respond_to?(:map) && !value.acts_like?(:string)
169
+ if value.respond_to?(:empty?) && value.empty?
170
+ c.quote(nil)
171
+ else
172
+ value.map { |v| c.quote(v) }.join(',')
173
+ end
174
+ else
175
+ c.quote(value)
176
+ end
177
+ end
178
+
179
+ def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
180
+ unless expected == provided
181
+ raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
182
+ end
183
+ end
184
+ end
185
+
186
+ # TODO: Deprecate this
187
+ def quoted_id
188
+ self.class.quote_value(id, column_for_attribute(self.class.primary_key))
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,64 @@
1
+ module ActiveRecord
2
+ # = Active Record Schema
3
+ #
4
+ # Allows programmers to programmatically define a schema in a portable
5
+ # DSL. This means you can define tables, indexes, etc. without using SQL
6
+ # directly, so your applications can more easily support multiple
7
+ # databases.
8
+ #
9
+ # Usage:
10
+ #
11
+ # ActiveRecord::Schema.define do
12
+ # create_table :authors do |t|
13
+ # t.string :name, null: false
14
+ # end
15
+ #
16
+ # add_index :authors, :name, :unique
17
+ #
18
+ # create_table :posts do |t|
19
+ # t.integer :author_id, null: false
20
+ # t.string :subject
21
+ # t.text :body
22
+ # t.boolean :private, default: false
23
+ # end
24
+ #
25
+ # add_index :posts, :author_id
26
+ # end
27
+ #
28
+ # ActiveRecord::Schema is only supported by database adapters that also
29
+ # support migrations, the two features being very similar.
30
+ class Schema < Migration
31
+
32
+ # Returns the migrations paths.
33
+ #
34
+ # ActiveRecord::Schema.new.migrations_paths
35
+ # # => ["db/migrate"] # Rails migration path by default.
36
+ def migrations_paths
37
+ ActiveRecord::Migrator.migrations_paths
38
+ end
39
+
40
+ def define(info, &block) # :nodoc:
41
+ instance_eval(&block)
42
+
43
+ unless info[:version].blank?
44
+ initialize_schema_migrations_table
45
+ connection.assume_migrated_upto_version(info[:version], migrations_paths)
46
+ end
47
+ end
48
+
49
+ # Eval the given block. All methods available to the current connection
50
+ # adapter are available within the block, so you can easily use the
51
+ # database definition DSL to build up your schema (+create_table+,
52
+ # +add_index+, etc.).
53
+ #
54
+ # The +info+ hash is optional, and if given is used to define metadata
55
+ # about the current schema (currently, only the schema's version):
56
+ #
57
+ # ActiveRecord::Schema.define(version: 20380119000001) do
58
+ # ...
59
+ # end
60
+ def self.define(info={}, &block)
61
+ new.define(info, &block)
62
+ end
63
+ end
64
+ end