activerecord 1.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (255) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +213 -0
  5. data/examples/performance.rb +172 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +180 -84
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +248 -0
  10. data/lib/active_record/associations/association_scope.rb +135 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +92 -0
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
  13. data/lib/active_record/associations/builder/association.rb +108 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  17. data/lib/active_record/associations/builder/has_many.rb +15 -0
  18. data/lib/active_record/associations/builder/has_one.rb +25 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +608 -0
  21. data/lib/active_record/associations/collection_proxy.rb +986 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
  23. data/lib/active_record/associations/has_many_association.rb +116 -85
  24. data/lib/active_record/associations/has_many_through_association.rb +197 -0
  25. data/lib/active_record/associations/has_one_association.rb +102 -0
  26. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +235 -0
  31. data/lib/active_record/associations/join_helper.rb +45 -0
  32. data/lib/active_record/associations/preloader/association.rb +121 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  42. data/lib/active_record/associations/preloader.rb +178 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +1437 -431
  46. data/lib/active_record/attribute_assignment.rb +201 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
  48. data/lib/active_record/attribute_methods/dirty.rb +118 -0
  49. data/lib/active_record/attribute_methods/primary_key.rb +122 -0
  50. data/lib/active_record/attribute_methods/query.rb +40 -0
  51. data/lib/active_record/attribute_methods/read.rb +107 -0
  52. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  53. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
  54. data/lib/active_record/attribute_methods/write.rb +63 -0
  55. data/lib/active_record/attribute_methods.rb +393 -0
  56. data/lib/active_record/autosave_association.rb +426 -0
  57. data/lib/active_record/base.rb +268 -930
  58. data/lib/active_record/callbacks.rb +203 -230
  59. data/lib/active_record/coders/yaml_column.rb +38 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  71. data/lib/active_record/connection_adapters/column.rb +318 -0
  72. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
  75. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  76. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  77. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  81. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  82. data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
  83. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  84. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
  85. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  86. data/lib/active_record/connection_handling.rb +98 -0
  87. data/lib/active_record/core.rb +463 -0
  88. data/lib/active_record/counter_cache.rb +122 -0
  89. data/lib/active_record/dynamic_matchers.rb +131 -0
  90. data/lib/active_record/errors.rb +213 -0
  91. data/lib/active_record/explain.rb +38 -0
  92. data/lib/active_record/explain_registry.rb +30 -0
  93. data/lib/active_record/explain_subscriber.rb +29 -0
  94. data/lib/active_record/fixture_set/file.rb +55 -0
  95. data/lib/active_record/fixtures.rb +892 -138
  96. data/lib/active_record/inheritance.rb +200 -0
  97. data/lib/active_record/integration.rb +60 -0
  98. data/lib/active_record/locale/en.yml +47 -0
  99. data/lib/active_record/locking/optimistic.rb +181 -0
  100. data/lib/active_record/locking/pessimistic.rb +77 -0
  101. data/lib/active_record/log_subscriber.rb +82 -0
  102. data/lib/active_record/migration/command_recorder.rb +164 -0
  103. data/lib/active_record/migration/join_table.rb +15 -0
  104. data/lib/active_record/migration.rb +1015 -0
  105. data/lib/active_record/model_schema.rb +345 -0
  106. data/lib/active_record/nested_attributes.rb +546 -0
  107. data/lib/active_record/null_relation.rb +65 -0
  108. data/lib/active_record/persistence.rb +509 -0
  109. data/lib/active_record/query_cache.rb +56 -0
  110. data/lib/active_record/querying.rb +62 -0
  111. data/lib/active_record/railtie.rb +205 -0
  112. data/lib/active_record/railties/console_sandbox.rb +5 -0
  113. data/lib/active_record/railties/controller_runtime.rb +50 -0
  114. data/lib/active_record/railties/databases.rake +402 -0
  115. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  116. data/lib/active_record/readonly_attributes.rb +30 -0
  117. data/lib/active_record/reflection.rb +544 -87
  118. data/lib/active_record/relation/batches.rb +93 -0
  119. data/lib/active_record/relation/calculations.rb +399 -0
  120. data/lib/active_record/relation/delegation.rb +125 -0
  121. data/lib/active_record/relation/finder_methods.rb +349 -0
  122. data/lib/active_record/relation/merger.rb +161 -0
  123. data/lib/active_record/relation/predicate_builder.rb +106 -0
  124. data/lib/active_record/relation/query_methods.rb +1044 -0
  125. data/lib/active_record/relation/spawn_methods.rb +73 -0
  126. data/lib/active_record/relation.rb +655 -0
  127. data/lib/active_record/result.rb +67 -0
  128. data/lib/active_record/runtime_registry.rb +17 -0
  129. data/lib/active_record/sanitization.rb +168 -0
  130. data/lib/active_record/schema.rb +65 -0
  131. data/lib/active_record/schema_dumper.rb +204 -0
  132. data/lib/active_record/schema_migration.rb +39 -0
  133. data/lib/active_record/scoping/default.rb +146 -0
  134. data/lib/active_record/scoping/named.rb +175 -0
  135. data/lib/active_record/scoping.rb +82 -0
  136. data/lib/active_record/serialization.rb +22 -0
  137. data/lib/active_record/serializers/xml_serializer.rb +197 -0
  138. data/lib/active_record/statement_cache.rb +26 -0
  139. data/lib/active_record/store.rb +156 -0
  140. data/lib/active_record/tasks/database_tasks.rb +203 -0
  141. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  142. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  143. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  146. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  147. data/lib/active_record/test_case.rb +96 -0
  148. data/lib/active_record/timestamp.rb +119 -0
  149. data/lib/active_record/transactions.rb +366 -69
  150. data/lib/active_record/translation.rb +22 -0
  151. data/lib/active_record/validations/associated.rb +49 -0
  152. data/lib/active_record/validations/presence.rb +65 -0
  153. data/lib/active_record/validations/uniqueness.rb +225 -0
  154. data/lib/active_record/validations.rb +64 -185
  155. data/lib/active_record/version.rb +11 -0
  156. data/lib/active_record.rb +149 -24
  157. data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
  158. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  159. data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
  160. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  161. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  162. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  163. data/lib/rails/generators/active_record.rb +23 -0
  164. metadata +261 -161
  165. data/CHANGELOG +0 -581
  166. data/README +0 -361
  167. data/RUNNING_UNIT_TESTS +0 -36
  168. data/dev-utils/eval_debugger.rb +0 -9
  169. data/examples/associations.png +0 -0
  170. data/examples/associations.rb +0 -87
  171. data/examples/shared_setup.rb +0 -15
  172. data/examples/validation.rb +0 -88
  173. data/install.rb +0 -60
  174. data/lib/active_record/associations/association_collection.rb +0 -70
  175. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
  176. data/lib/active_record/deprecated_associations.rb +0 -70
  177. data/lib/active_record/observer.rb +0 -71
  178. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  179. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  180. data/lib/active_record/support/clean_logger.rb +0 -10
  181. data/lib/active_record/support/inflector.rb +0 -70
  182. data/lib/active_record/vendor/mysql.rb +0 -1117
  183. data/lib/active_record/vendor/simple.rb +0 -702
  184. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  185. data/lib/active_record/wrappings.rb +0 -59
  186. data/rakefile +0 -122
  187. data/test/abstract_unit.rb +0 -16
  188. data/test/aggregations_test.rb +0 -34
  189. data/test/all.sh +0 -8
  190. data/test/associations_test.rb +0 -477
  191. data/test/base_test.rb +0 -513
  192. data/test/class_inheritable_attributes_test.rb +0 -33
  193. data/test/connections/native_mysql/connection.rb +0 -24
  194. data/test/connections/native_postgresql/connection.rb +0 -24
  195. data/test/connections/native_sqlite/connection.rb +0 -24
  196. data/test/deprecated_associations_test.rb +0 -336
  197. data/test/finder_test.rb +0 -67
  198. data/test/fixtures/accounts/signals37 +0 -3
  199. data/test/fixtures/accounts/unknown +0 -2
  200. data/test/fixtures/auto_id.rb +0 -4
  201. data/test/fixtures/column_name.rb +0 -3
  202. data/test/fixtures/companies/first_client +0 -6
  203. data/test/fixtures/companies/first_firm +0 -4
  204. data/test/fixtures/companies/second_client +0 -6
  205. data/test/fixtures/company.rb +0 -37
  206. data/test/fixtures/company_in_module.rb +0 -33
  207. data/test/fixtures/course.rb +0 -3
  208. data/test/fixtures/courses/java +0 -2
  209. data/test/fixtures/courses/ruby +0 -2
  210. data/test/fixtures/customer.rb +0 -30
  211. data/test/fixtures/customers/david +0 -6
  212. data/test/fixtures/db_definitions/mysql.sql +0 -96
  213. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  214. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  215. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  216. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  217. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  218. data/test/fixtures/default.rb +0 -2
  219. data/test/fixtures/developer.rb +0 -8
  220. data/test/fixtures/developers/david +0 -2
  221. data/test/fixtures/developers/jamis +0 -2
  222. data/test/fixtures/developers_projects/david_action_controller +0 -2
  223. data/test/fixtures/developers_projects/david_active_record +0 -2
  224. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  225. data/test/fixtures/entrant.rb +0 -3
  226. data/test/fixtures/entrants/first +0 -3
  227. data/test/fixtures/entrants/second +0 -3
  228. data/test/fixtures/entrants/third +0 -3
  229. data/test/fixtures/fixture_database.sqlite +0 -0
  230. data/test/fixtures/fixture_database_2.sqlite +0 -0
  231. data/test/fixtures/movie.rb +0 -5
  232. data/test/fixtures/movies/first +0 -2
  233. data/test/fixtures/movies/second +0 -2
  234. data/test/fixtures/project.rb +0 -3
  235. data/test/fixtures/projects/action_controller +0 -2
  236. data/test/fixtures/projects/active_record +0 -2
  237. data/test/fixtures/reply.rb +0 -21
  238. data/test/fixtures/subscriber.rb +0 -5
  239. data/test/fixtures/subscribers/first +0 -2
  240. data/test/fixtures/subscribers/second +0 -2
  241. data/test/fixtures/topic.rb +0 -20
  242. data/test/fixtures/topics/first +0 -9
  243. data/test/fixtures/topics/second +0 -8
  244. data/test/fixtures_test.rb +0 -20
  245. data/test/inflector_test.rb +0 -104
  246. data/test/inheritance_test.rb +0 -125
  247. data/test/lifecycle_test.rb +0 -110
  248. data/test/modules_test.rb +0 -21
  249. data/test/multiple_db_test.rb +0 -46
  250. data/test/pk_test.rb +0 -57
  251. data/test/reflection_test.rb +0 -78
  252. data/test/thread_safety_test.rb +0 -33
  253. data/test/transactions_test.rb +0 -83
  254. data/test/unconnected_test.rb +0 -24
  255. data/test/validations_test.rb +0 -126
@@ -0,0 +1,67 @@
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
+ # x = ActiveRecord::Base.connection.exec_query('SELECT * FROM foo')
7
+ # x # => #<ActiveRecord::Result:0xdeadbeef>
8
+ class Result
9
+ include Enumerable
10
+
11
+ attr_reader :columns, :rows, :column_types
12
+
13
+ def initialize(columns, rows, column_types = {})
14
+ @columns = columns
15
+ @rows = rows
16
+ @hash_rows = nil
17
+ @column_types = column_types
18
+ end
19
+
20
+ def each
21
+ hash_rows.each { |row| yield row }
22
+ end
23
+
24
+ def to_hash
25
+ hash_rows
26
+ end
27
+
28
+ alias :map! :map
29
+ alias :collect! :map
30
+
31
+ # Returns true if there are no records.
32
+ def empty?
33
+ rows.empty?
34
+ end
35
+
36
+ def to_ary
37
+ hash_rows
38
+ end
39
+
40
+ def [](idx)
41
+ hash_rows[idx]
42
+ end
43
+
44
+ def last
45
+ hash_rows.last
46
+ end
47
+
48
+ def initialize_copy(other)
49
+ @columns = columns.dup
50
+ @rows = rows.dup
51
+ @hash_rows = nil
52
+ end
53
+
54
+ private
55
+ def hash_rows
56
+ @hash_rows ||=
57
+ begin
58
+ # We freeze the strings to prevent them getting duped when
59
+ # used as keys in ActiveRecord::Base's @attributes hash
60
+ columns = @columns.map { |c| c.dup.freeze }
61
+ @rows.map { |row|
62
+ Hash[columns.zip(row)]
63
+ }
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,17 @@
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
+ end
17
+ end
@@ -0,0 +1,168 @@
1
+ module ActiveRecord
2
+ module Sanitization
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def quote_value(value, column = nil) #: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
+
33
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
34
+ # them into a valid SQL fragment for a SET clause.
35
+ # { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
36
+ def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
37
+ case assignments
38
+ when Array; sanitize_sql_array(assignments)
39
+ when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
40
+ else assignments
41
+ end
42
+ end
43
+
44
+ # Accepts a hash of SQL conditions and replaces those attributes
45
+ # that correspond to a +composed_of+ relationship with their expanded
46
+ # aggregate attribute values.
47
+ # Given:
48
+ # class Person < ActiveRecord::Base
49
+ # composed_of :address, class_name: "Address",
50
+ # mapping: [%w(address_street street), %w(address_city city)]
51
+ # end
52
+ # Then:
53
+ # { address: Address.new("813 abc st.", "chicago") }
54
+ # # => { address_street: "813 abc st.", address_city: "chicago" }
55
+ def expand_hash_conditions_for_aggregates(attrs)
56
+ expanded_attrs = {}
57
+ attrs.each do |attr, value|
58
+ if aggregation = reflect_on_aggregation(attr.to_sym)
59
+ mapping = aggregation.mapping
60
+ mapping.each do |field_attr, aggregate_attr|
61
+ if mapping.size == 1 && !value.respond_to?(aggregate_attr)
62
+ expanded_attrs[field_attr] = value
63
+ else
64
+ expanded_attrs[field_attr] = value.send(aggregate_attr)
65
+ end
66
+ end
67
+ else
68
+ expanded_attrs[attr] = value
69
+ end
70
+ end
71
+ expanded_attrs
72
+ end
73
+
74
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
75
+ # { name: "foo'bar", group_id: 4 }
76
+ # # => "name='foo''bar' and group_id= 4"
77
+ # { status: nil, group_id: [1,2,3] }
78
+ # # => "status IS NULL and group_id IN (1,2,3)"
79
+ # { age: 13..18 }
80
+ # # => "age BETWEEN 13 AND 18"
81
+ # { 'other_records.id' => 7 }
82
+ # # => "`other_records`.`id` = 7"
83
+ # { other_records: { id: 7 } }
84
+ # # => "`other_records`.`id` = 7"
85
+ # And for value objects on a composed_of relationship:
86
+ # { address: Address.new("123 abc st.", "chicago") }
87
+ # # => "address_street='123 abc st.' and address_city='chicago'"
88
+ def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
89
+ attrs = expand_hash_conditions_for_aggregates(attrs)
90
+
91
+ table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
92
+ PredicateBuilder.build_from_hash(self.class, attrs, table).map { |b|
93
+ connection.visitor.accept b
94
+ }.join(' AND ')
95
+ end
96
+ alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
97
+
98
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
99
+ # { status: nil, group_id: 1 }
100
+ # # => "status = NULL , group_id = 1"
101
+ def sanitize_sql_hash_for_assignment(attrs, table)
102
+ attrs.map do |attr, value|
103
+ "#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value)}"
104
+ end.join(', ')
105
+ end
106
+
107
+ # Accepts an array of conditions. The array has each value
108
+ # sanitized and interpolated into the SQL statement.
109
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
110
+ def sanitize_sql_array(ary)
111
+ statement, *values = ary
112
+ if values.first.is_a?(Hash) && statement =~ /:\w+/
113
+ replace_named_bind_variables(statement, values.first)
114
+ elsif statement.include?('?')
115
+ replace_bind_variables(statement, values)
116
+ elsif statement.blank?
117
+ statement
118
+ else
119
+ statement % values.collect { |value| connection.quote_string(value.to_s) }
120
+ end
121
+ end
122
+
123
+ alias_method :sanitize_conditions, :sanitize_sql
124
+
125
+ def replace_bind_variables(statement, values) #:nodoc:
126
+ raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
127
+ bound = values.dup
128
+ c = connection
129
+ statement.gsub('?') { quote_bound_value(bound.shift, c) }
130
+ end
131
+
132
+ def replace_named_bind_variables(statement, bind_vars) #:nodoc:
133
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do
134
+ if $1 == ':' # skip postgresql casts
135
+ $& # return the whole match
136
+ elsif bind_vars.include?(match = $2.to_sym)
137
+ quote_bound_value(bind_vars[match])
138
+ else
139
+ raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
140
+ end
141
+ end
142
+ end
143
+
144
+ def quote_bound_value(value, c = connection) #:nodoc:
145
+ if value.respond_to?(:map) && !value.acts_like?(:string)
146
+ if value.respond_to?(:empty?) && value.empty?
147
+ c.quote(nil)
148
+ else
149
+ value.map { |v| c.quote(v) }.join(',')
150
+ end
151
+ else
152
+ c.quote(value)
153
+ end
154
+ end
155
+
156
+ def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
157
+ unless expected == provided
158
+ raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
159
+ end
160
+ end
161
+ end
162
+
163
+ # TODO: Deprecate this
164
+ def quoted_id
165
+ self.class.quote_value(id, column_for_attribute(self.class.primary_key))
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,65 @@
1
+
2
+ module ActiveRecord
3
+ # = Active Record Schema
4
+ #
5
+ # Allows programmers to programmatically define a schema in a portable
6
+ # DSL. This means you can define tables, indexes, etc. without using SQL
7
+ # directly, so your applications can more easily support multiple
8
+ # databases.
9
+ #
10
+ # Usage:
11
+ #
12
+ # ActiveRecord::Schema.define do
13
+ # create_table :authors do |t|
14
+ # t.string :name, null: false
15
+ # end
16
+ #
17
+ # add_index :authors, :name, :unique
18
+ #
19
+ # create_table :posts do |t|
20
+ # t.integer :author_id, null: false
21
+ # t.string :subject
22
+ # t.text :body
23
+ # t.boolean :private, default: false
24
+ # end
25
+ #
26
+ # add_index :posts, :author_id
27
+ # end
28
+ #
29
+ # ActiveRecord::Schema is only supported by database adapters that also
30
+ # support migrations, the two features being very similar.
31
+ class Schema < Migration
32
+
33
+ # Returns the migrations paths.
34
+ #
35
+ # ActiveRecord::Schema.new.migrations_paths
36
+ # # => ["db/migrate"] # Rails migration path by default.
37
+ def migrations_paths
38
+ ActiveRecord::Migrator.migrations_paths
39
+ end
40
+
41
+ def define(info, &block) # :nodoc:
42
+ instance_eval(&block)
43
+
44
+ unless info[:version].blank?
45
+ initialize_schema_migrations_table
46
+ connection.assume_migrated_upto_version(info[:version], migrations_paths)
47
+ end
48
+ end
49
+
50
+ # Eval the given block. All methods available to the current connection
51
+ # adapter are available within the block, so you can easily use the
52
+ # database definition DSL to build up your schema (+create_table+,
53
+ # +add_index+, etc.).
54
+ #
55
+ # The +info+ hash is optional, and if given is used to define metadata
56
+ # about the current schema (currently, only the schema's version):
57
+ #
58
+ # ActiveRecord::Schema.define(version: 20380119000001) do
59
+ # ...
60
+ # end
61
+ def self.define(info={}, &block)
62
+ new.define(info, &block)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,204 @@
1
+ require 'stringio'
2
+ require 'active_support/core_ext/big_decimal'
3
+
4
+ module ActiveRecord
5
+ # = Active Record Schema Dumper
6
+ #
7
+ # This class is used to dump the database schema for some connection to some
8
+ # output format (i.e., ActiveRecord::Schema).
9
+ class SchemaDumper #:nodoc:
10
+ private_class_method :new
11
+
12
+ ##
13
+ # :singleton-method:
14
+ # A list of tables which should not be dumped to the schema.
15
+ # Acceptable values are strings as well as regexp.
16
+ # This setting is only used if ActiveRecord::Base.schema_format == :ruby
17
+ cattr_accessor :ignore_tables
18
+ @@ignore_tables = []
19
+
20
+ def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT)
21
+ new(connection).dump(stream)
22
+ stream
23
+ end
24
+
25
+ def dump(stream)
26
+ header(stream)
27
+ extensions(stream)
28
+ tables(stream)
29
+ trailer(stream)
30
+ stream
31
+ end
32
+
33
+ private
34
+
35
+ def initialize(connection)
36
+ @connection = connection
37
+ @types = @connection.native_database_types
38
+ @version = Migrator::current_version rescue nil
39
+ end
40
+
41
+ def header(stream)
42
+ define_params = @version ? "version: #{@version}" : ""
43
+
44
+ if stream.respond_to?(:external_encoding) && stream.external_encoding
45
+ stream.puts "# encoding: #{stream.external_encoding.name}"
46
+ end
47
+
48
+ stream.puts <<HEADER
49
+ # This file is auto-generated from the current state of the database. Instead
50
+ # of editing this file, please use the migrations feature of Active Record to
51
+ # incrementally modify your database, and then regenerate this schema definition.
52
+ #
53
+ # Note that this schema.rb definition is the authoritative source for your
54
+ # database schema. If you need to create the application database on another
55
+ # system, you should be using db:schema:load, not running all the migrations
56
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
57
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
58
+ #
59
+ # It's strongly recommended that you check this file into your version control system.
60
+
61
+ ActiveRecord::Schema.define(#{define_params}) do
62
+
63
+ HEADER
64
+ end
65
+
66
+ def trailer(stream)
67
+ stream.puts "end"
68
+ end
69
+
70
+ def extensions(stream)
71
+ return unless @connection.supports_extensions?
72
+ extensions = @connection.extensions
73
+ if extensions.any?
74
+ stream.puts " # These are extensions that must be enabled in order to support this database"
75
+ extensions.each do |extension|
76
+ stream.puts " enable_extension #{extension.inspect}"
77
+ end
78
+ stream.puts
79
+ end
80
+ end
81
+
82
+ def tables(stream)
83
+ @connection.tables.sort.each do |tbl|
84
+ next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
85
+ case ignored
86
+ when String; remove_prefix_and_suffix(tbl) == ignored
87
+ when Regexp; remove_prefix_and_suffix(tbl) =~ ignored
88
+ else
89
+ raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
90
+ end
91
+ end
92
+ table(tbl, stream)
93
+ end
94
+ end
95
+
96
+ def table(table, stream)
97
+ columns = @connection.columns(table)
98
+ begin
99
+ tbl = StringIO.new
100
+
101
+ # first dump primary key column
102
+ if @connection.respond_to?(:pk_and_sequence_for)
103
+ pk, _ = @connection.pk_and_sequence_for(table)
104
+ elsif @connection.respond_to?(:primary_key)
105
+ pk = @connection.primary_key(table)
106
+ end
107
+
108
+ tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
109
+ if columns.detect { |c| c.name == pk }
110
+ if pk != 'id'
111
+ tbl.print %Q(, primary_key: "#{pk}")
112
+ end
113
+ else
114
+ tbl.print ", id: false"
115
+ end
116
+ tbl.print ", force: true"
117
+ tbl.puts " do |t|"
118
+
119
+ # then dump all non-primary key columns
120
+ column_specs = columns.map do |column|
121
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
122
+ next if column.name == pk
123
+ @connection.column_spec(column, @types)
124
+ end.compact
125
+
126
+ # find all migration keys used in this table
127
+ keys = @connection.migration_keys
128
+
129
+ # figure out the lengths for each column based on above keys
130
+ lengths = keys.map { |key|
131
+ column_specs.map { |spec|
132
+ spec[key] ? spec[key].length + 2 : 0
133
+ }.max
134
+ }
135
+
136
+ # the string we're going to sprintf our values against, with standardized column widths
137
+ format_string = lengths.map{ |len| "%-#{len}s" }
138
+
139
+ # find the max length for the 'type' column, which is special
140
+ type_length = column_specs.map{ |column| column[:type].length }.max
141
+
142
+ # add column type definition to our format string
143
+ format_string.unshift " t.%-#{type_length}s "
144
+
145
+ format_string *= ''
146
+
147
+ column_specs.each do |colspec|
148
+ values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
149
+ values.unshift colspec[:type]
150
+ tbl.print((format_string % values).gsub(/,\s*$/, ''))
151
+ tbl.puts
152
+ end
153
+
154
+ tbl.puts " end"
155
+ tbl.puts
156
+
157
+ indexes(table, tbl)
158
+
159
+ tbl.rewind
160
+ stream.print tbl.read
161
+ rescue => e
162
+ stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
163
+ stream.puts "# #{e.message}"
164
+ stream.puts
165
+ end
166
+
167
+ stream
168
+ end
169
+
170
+ def indexes(table, stream)
171
+ if (indexes = @connection.indexes(table)).any?
172
+ add_index_statements = indexes.map do |index|
173
+ statement_parts = [
174
+ ('add_index ' + remove_prefix_and_suffix(index.table).inspect),
175
+ index.columns.inspect,
176
+ ('name: ' + index.name.inspect),
177
+ ]
178
+ statement_parts << 'unique: true' if index.unique
179
+
180
+ index_lengths = (index.lengths || []).compact
181
+ statement_parts << ('length: ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
182
+
183
+ index_orders = (index.orders || {})
184
+ statement_parts << ('order: ' + index.orders.inspect) unless index_orders.empty?
185
+
186
+ statement_parts << ('where: ' + index.where.inspect) if index.where
187
+
188
+ statement_parts << ('using: ' + index.using.inspect) if index.using
189
+
190
+ statement_parts << ('type: ' + index.type.inspect) if index.type
191
+
192
+ ' ' + statement_parts.join(', ')
193
+ end
194
+
195
+ stream.puts add_index_statements.sort.join("\n")
196
+ stream.puts
197
+ end
198
+ end
199
+
200
+ def remove_prefix_and_suffix(table)
201
+ table.gsub(/^(#{ActiveRecord::Base.table_name_prefix})(.+)(#{ActiveRecord::Base.table_name_suffix})$/, "\\2")
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,39 @@
1
+ require 'active_record/scoping/default'
2
+ require 'active_record/scoping/named'
3
+ require 'active_record/base'
4
+
5
+ module ActiveRecord
6
+ class SchemaMigration < ActiveRecord::Base
7
+
8
+ def self.table_name
9
+ "#{Base.table_name_prefix}schema_migrations#{Base.table_name_suffix}"
10
+ end
11
+
12
+ def self.index_name
13
+ "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
14
+ end
15
+
16
+ def self.create_table(limit=nil)
17
+ unless connection.table_exists?(table_name)
18
+ version_options = {null: false}
19
+ version_options[:limit] = limit if limit
20
+
21
+ connection.create_table(table_name, id: false) do |t|
22
+ t.column :version, :string, version_options
23
+ end
24
+ connection.add_index table_name, :version, unique: true, name: index_name
25
+ end
26
+ end
27
+
28
+ def self.drop_table
29
+ if connection.table_exists?(table_name)
30
+ connection.remove_index table_name, name: index_name
31
+ connection.drop_table(table_name)
32
+ end
33
+ end
34
+
35
+ def version
36
+ super.to_i
37
+ end
38
+ end
39
+ end