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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +2102 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +213 -0
- data/examples/performance.rb +172 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record/aggregations.rb +180 -84
- data/lib/active_record/associations/alias_tracker.rb +76 -0
- data/lib/active_record/associations/association.rb +248 -0
- data/lib/active_record/associations/association_scope.rb +135 -0
- data/lib/active_record/associations/belongs_to_association.rb +92 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
- data/lib/active_record/associations/builder/association.rb +108 -0
- data/lib/active_record/associations/builder/belongs_to.rb +98 -0
- data/lib/active_record/associations/builder/collection_association.rb +89 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
- data/lib/active_record/associations/builder/has_many.rb +15 -0
- data/lib/active_record/associations/builder/has_one.rb +25 -0
- data/lib/active_record/associations/builder/singular_association.rb +32 -0
- data/lib/active_record/associations/collection_association.rb +608 -0
- data/lib/active_record/associations/collection_proxy.rb +986 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
- data/lib/active_record/associations/has_many_association.rb +116 -85
- data/lib/active_record/associations/has_many_through_association.rb +197 -0
- data/lib/active_record/associations/has_one_association.rb +102 -0
- data/lib/active_record/associations/has_one_through_association.rb +36 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
- data/lib/active_record/associations/join_dependency.rb +235 -0
- data/lib/active_record/associations/join_helper.rb +45 -0
- data/lib/active_record/associations/preloader/association.rb +121 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +63 -0
- data/lib/active_record/associations/preloader.rb +178 -0
- data/lib/active_record/associations/singular_association.rb +64 -0
- data/lib/active_record/associations/through_association.rb +87 -0
- data/lib/active_record/associations.rb +1437 -431
- data/lib/active_record/attribute_assignment.rb +201 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
- data/lib/active_record/attribute_methods/dirty.rb +118 -0
- data/lib/active_record/attribute_methods/primary_key.rb +122 -0
- data/lib/active_record/attribute_methods/query.rb +40 -0
- data/lib/active_record/attribute_methods/read.rb +107 -0
- data/lib/active_record/attribute_methods/serialization.rb +162 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
- data/lib/active_record/attribute_methods/write.rb +63 -0
- data/lib/active_record/attribute_methods.rb +393 -0
- data/lib/active_record/autosave_association.rb +426 -0
- data/lib/active_record/base.rb +268 -930
- data/lib/active_record/callbacks.rb +203 -230
- data/lib/active_record/coders/yaml_column.rb +38 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
- data/lib/active_record/connection_adapters/column.rb +318 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
- data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +463 -0
- data/lib/active_record/counter_cache.rb +122 -0
- data/lib/active_record/dynamic_matchers.rb +131 -0
- data/lib/active_record/errors.rb +213 -0
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +892 -138
- data/lib/active_record/inheritance.rb +200 -0
- data/lib/active_record/integration.rb +60 -0
- data/lib/active_record/locale/en.yml +47 -0
- data/lib/active_record/locking/optimistic.rb +181 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/log_subscriber.rb +82 -0
- data/lib/active_record/migration/command_recorder.rb +164 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +1015 -0
- data/lib/active_record/model_schema.rb +345 -0
- data/lib/active_record/nested_attributes.rb +546 -0
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +509 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/querying.rb +62 -0
- data/lib/active_record/railtie.rb +205 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +50 -0
- data/lib/active_record/railties/databases.rake +402 -0
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +30 -0
- data/lib/active_record/reflection.rb +544 -87
- data/lib/active_record/relation/batches.rb +93 -0
- data/lib/active_record/relation/calculations.rb +399 -0
- data/lib/active_record/relation/delegation.rb +125 -0
- data/lib/active_record/relation/finder_methods.rb +349 -0
- data/lib/active_record/relation/merger.rb +161 -0
- data/lib/active_record/relation/predicate_builder.rb +106 -0
- data/lib/active_record/relation/query_methods.rb +1044 -0
- data/lib/active_record/relation/spawn_methods.rb +73 -0
- data/lib/active_record/relation.rb +655 -0
- data/lib/active_record/result.rb +67 -0
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +168 -0
- data/lib/active_record/schema.rb +65 -0
- data/lib/active_record/schema_dumper.rb +204 -0
- data/lib/active_record/schema_migration.rb +39 -0
- data/lib/active_record/scoping/default.rb +146 -0
- data/lib/active_record/scoping/named.rb +175 -0
- data/lib/active_record/scoping.rb +82 -0
- data/lib/active_record/serialization.rb +22 -0
- data/lib/active_record/serializers/xml_serializer.rb +197 -0
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +156 -0
- data/lib/active_record/tasks/database_tasks.rb +203 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +96 -0
- data/lib/active_record/timestamp.rb +119 -0
- data/lib/active_record/transactions.rb +366 -69
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +49 -0
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +225 -0
- data/lib/active_record/validations.rb +64 -185
- data/lib/active_record/version.rb +11 -0
- data/lib/active_record.rb +149 -24
- data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
- data/lib/rails/generators/active_record.rb +23 -0
- metadata +261 -161
- data/CHANGELOG +0 -581
- data/README +0 -361
- data/RUNNING_UNIT_TESTS +0 -36
- data/dev-utils/eval_debugger.rb +0 -9
- data/examples/associations.png +0 -0
- data/examples/associations.rb +0 -87
- data/examples/shared_setup.rb +0 -15
- data/examples/validation.rb +0 -88
- data/install.rb +0 -60
- data/lib/active_record/associations/association_collection.rb +0 -70
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
- data/lib/active_record/deprecated_associations.rb +0 -70
- data/lib/active_record/observer.rb +0 -71
- data/lib/active_record/support/class_attribute_accessors.rb +0 -43
- data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
- data/lib/active_record/support/clean_logger.rb +0 -10
- data/lib/active_record/support/inflector.rb +0 -70
- data/lib/active_record/vendor/mysql.rb +0 -1117
- data/lib/active_record/vendor/simple.rb +0 -702
- data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
- data/lib/active_record/wrappings.rb +0 -59
- data/rakefile +0 -122
- data/test/abstract_unit.rb +0 -16
- data/test/aggregations_test.rb +0 -34
- data/test/all.sh +0 -8
- data/test/associations_test.rb +0 -477
- data/test/base_test.rb +0 -513
- data/test/class_inheritable_attributes_test.rb +0 -33
- data/test/connections/native_mysql/connection.rb +0 -24
- data/test/connections/native_postgresql/connection.rb +0 -24
- data/test/connections/native_sqlite/connection.rb +0 -24
- data/test/deprecated_associations_test.rb +0 -336
- data/test/finder_test.rb +0 -67
- data/test/fixtures/accounts/signals37 +0 -3
- data/test/fixtures/accounts/unknown +0 -2
- data/test/fixtures/auto_id.rb +0 -4
- data/test/fixtures/column_name.rb +0 -3
- data/test/fixtures/companies/first_client +0 -6
- data/test/fixtures/companies/first_firm +0 -4
- data/test/fixtures/companies/second_client +0 -6
- data/test/fixtures/company.rb +0 -37
- data/test/fixtures/company_in_module.rb +0 -33
- data/test/fixtures/course.rb +0 -3
- data/test/fixtures/courses/java +0 -2
- data/test/fixtures/courses/ruby +0 -2
- data/test/fixtures/customer.rb +0 -30
- data/test/fixtures/customers/david +0 -6
- data/test/fixtures/db_definitions/mysql.sql +0 -96
- data/test/fixtures/db_definitions/mysql2.sql +0 -4
- data/test/fixtures/db_definitions/postgresql.sql +0 -113
- data/test/fixtures/db_definitions/postgresql2.sql +0 -4
- data/test/fixtures/db_definitions/sqlite.sql +0 -85
- data/test/fixtures/db_definitions/sqlite2.sql +0 -4
- data/test/fixtures/default.rb +0 -2
- data/test/fixtures/developer.rb +0 -8
- data/test/fixtures/developers/david +0 -2
- data/test/fixtures/developers/jamis +0 -2
- data/test/fixtures/developers_projects/david_action_controller +0 -2
- data/test/fixtures/developers_projects/david_active_record +0 -2
- data/test/fixtures/developers_projects/jamis_active_record +0 -2
- data/test/fixtures/entrant.rb +0 -3
- data/test/fixtures/entrants/first +0 -3
- data/test/fixtures/entrants/second +0 -3
- data/test/fixtures/entrants/third +0 -3
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/movie.rb +0 -5
- data/test/fixtures/movies/first +0 -2
- data/test/fixtures/movies/second +0 -2
- data/test/fixtures/project.rb +0 -3
- data/test/fixtures/projects/action_controller +0 -2
- data/test/fixtures/projects/active_record +0 -2
- data/test/fixtures/reply.rb +0 -21
- data/test/fixtures/subscriber.rb +0 -5
- data/test/fixtures/subscribers/first +0 -2
- data/test/fixtures/subscribers/second +0 -2
- data/test/fixtures/topic.rb +0 -20
- data/test/fixtures/topics/first +0 -9
- data/test/fixtures/topics/second +0 -8
- data/test/fixtures_test.rb +0 -20
- data/test/inflector_test.rb +0 -104
- data/test/inheritance_test.rb +0 -125
- data/test/lifecycle_test.rb +0 -110
- data/test/modules_test.rb +0 -21
- data/test/multiple_db_test.rb +0 -46
- data/test/pk_test.rb +0 -57
- data/test/reflection_test.rb +0 -78
- data/test/thread_safety_test.rb +0 -33
- data/test/transactions_test.rb +0 -83
- data/test/unconnected_test.rb +0 -24
- 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
|