activerecord 3.2.22.4 → 4.0.13
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 +4 -4
- data/CHANGELOG.md +2799 -617
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -32
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations/alias_tracker.rb +4 -2
- data/lib/active_record/associations/association.rb +60 -46
- data/lib/active_record/associations/association_scope.rb +46 -40
- data/lib/active_record/associations/belongs_to_association.rb +17 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +73 -56
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +130 -96
- data/lib/active_record/associations/collection_proxy.rb +916 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
- data/lib/active_record/associations/has_many_association.rb +35 -8
- data/lib/active_record/associations/has_many_through_association.rb +37 -17
- data/lib/active_record/associations/has_one_association.rb +42 -19
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
- data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
- data/lib/active_record/associations/join_dependency.rb +30 -9
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/preloader.rb +20 -43
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +223 -282
- data/lib/active_record/attribute_assignment.rb +134 -154
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +36 -29
- data/lib/active_record/attribute_methods/primary_key.rb +45 -31
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +67 -90
- data/lib/active_record/attribute_methods/serialization.rb +133 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
- data/lib/active_record/attribute_methods/write.rb +34 -39
- data/lib/active_record/attribute_methods.rb +268 -108
- data/lib/active_record/autosave_association.rb +80 -73
- data/lib/active_record/base.rb +54 -451
- data/lib/active_record/callbacks.rb +60 -22
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
- data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
- data/lib/active_record/connection_adapters/column.rb +67 -36
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
- data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
- data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +472 -0
- data/lib/active_record/counter_cache.rb +107 -108
- data/lib/active_record/dynamic_matchers.rb +115 -63
- data/lib/active_record/errors.rb +36 -18
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +8 -4
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +159 -155
- data/lib/active_record/inheritance.rb +93 -59
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +39 -43
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration/command_recorder.rb +102 -33
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +411 -173
- data/lib/active_record/model_schema.rb +81 -94
- data/lib/active_record/nested_attributes.rb +173 -131
- data/lib/active_record/null_relation.rb +67 -0
- data/lib/active_record/persistence.rb +254 -106
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +113 -38
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +115 -368
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +110 -61
- data/lib/active_record/relation/batches.rb +29 -29
- data/lib/active_record/relation/calculations.rb +155 -125
- data/lib/active_record/relation/delegation.rb +94 -18
- data/lib/active_record/relation/finder_methods.rb +151 -203
- data/lib/active_record/relation/merger.rb +188 -0
- data/lib/active_record/relation/predicate_builder.rb +85 -42
- data/lib/active_record/relation/query_methods.rb +793 -146
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/relation.rb +293 -173
- data/lib/active_record/result.rb +48 -7
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +41 -54
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +41 -41
- data/lib/active_record/schema_migration.rb +46 -0
- data/lib/active_record/scoping/default.rb +56 -52
- data/lib/active_record/scoping/named.rb +78 -103
- data/lib/active_record/scoping.rb +54 -124
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +131 -15
- data/lib/active_record/tasks/database_tasks.rb +204 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +144 -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 +67 -38
- data/lib/active_record/timestamp.rb +16 -11
- data/lib/active_record/transactions.rb +73 -51
- data/lib/active_record/validations/associated.rb +19 -13
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +110 -57
- data/lib/active_record/validations.rb +18 -17
- data/lib/active_record/version.rb +7 -6
- data/lib/active_record.rb +63 -45
- data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -5
- metadata +43 -29
- data/examples/associations.png +0 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,583 +0,0 @@
|
|
1
|
-
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
-
require 'active_record/connection_adapters/statement_pool'
|
3
|
-
require 'active_support/core_ext/string/encoding'
|
4
|
-
require 'arel/visitors/bind_visitor'
|
5
|
-
|
6
|
-
module ActiveRecord
|
7
|
-
module ConnectionAdapters #:nodoc:
|
8
|
-
class SQLiteColumn < Column #:nodoc:
|
9
|
-
class << self
|
10
|
-
def binary_to_string(value)
|
11
|
-
if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT
|
12
|
-
value = value.force_encoding(Encoding::ASCII_8BIT)
|
13
|
-
end
|
14
|
-
value
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
# The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby
|
20
|
-
# drivers (available both as gems and from http://rubyforge.org/projects/sqlite-ruby/).
|
21
|
-
#
|
22
|
-
# Options:
|
23
|
-
#
|
24
|
-
# * <tt>:database</tt> - Path to the database file.
|
25
|
-
class SQLiteAdapter < AbstractAdapter
|
26
|
-
class Version
|
27
|
-
include Comparable
|
28
|
-
|
29
|
-
def initialize(version_string)
|
30
|
-
@version = version_string.split('.').map { |v| v.to_i }
|
31
|
-
end
|
32
|
-
|
33
|
-
def <=>(version_string)
|
34
|
-
@version <=> version_string.split('.').map { |v| v.to_i }
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class StatementPool < ConnectionAdapters::StatementPool
|
39
|
-
def initialize(connection, max)
|
40
|
-
super
|
41
|
-
@cache = Hash.new { |h,pid| h[pid] = {} }
|
42
|
-
end
|
43
|
-
|
44
|
-
def each(&block); cache.each(&block); end
|
45
|
-
def key?(key); cache.key?(key); end
|
46
|
-
def [](key); cache[key]; end
|
47
|
-
def length; cache.length; end
|
48
|
-
|
49
|
-
def []=(sql, key)
|
50
|
-
while @max <= cache.size
|
51
|
-
dealloc(cache.shift.last[:stmt])
|
52
|
-
end
|
53
|
-
cache[sql] = key
|
54
|
-
end
|
55
|
-
|
56
|
-
def clear
|
57
|
-
cache.values.each do |hash|
|
58
|
-
dealloc hash[:stmt]
|
59
|
-
end
|
60
|
-
cache.clear
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
def cache
|
65
|
-
@cache[$$]
|
66
|
-
end
|
67
|
-
|
68
|
-
def dealloc(stmt)
|
69
|
-
stmt.close unless stmt.closed?
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
class BindSubstitution < Arel::Visitors::SQLite # :nodoc:
|
74
|
-
include Arel::Visitors::BindVisitor
|
75
|
-
end
|
76
|
-
|
77
|
-
def initialize(connection, logger, config)
|
78
|
-
super(connection, logger)
|
79
|
-
@statements = StatementPool.new(@connection,
|
80
|
-
config.fetch(:statement_limit) { 1000 })
|
81
|
-
@config = config
|
82
|
-
|
83
|
-
if config.fetch(:prepared_statements) { true }
|
84
|
-
@visitor = Arel::Visitors::SQLite.new self
|
85
|
-
else
|
86
|
-
@visitor = BindSubstitution.new self
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def adapter_name #:nodoc:
|
91
|
-
'SQLite'
|
92
|
-
end
|
93
|
-
|
94
|
-
# Returns true if SQLite version is '2.0.0' or greater, false otherwise.
|
95
|
-
def supports_ddl_transactions?
|
96
|
-
sqlite_version >= '2.0.0'
|
97
|
-
end
|
98
|
-
|
99
|
-
# Returns true if SQLite version is '3.6.8' or greater, false otherwise.
|
100
|
-
def supports_savepoints?
|
101
|
-
sqlite_version >= '3.6.8'
|
102
|
-
end
|
103
|
-
|
104
|
-
# Returns true, since this connection adapter supports prepared statement
|
105
|
-
# caching.
|
106
|
-
def supports_statement_cache?
|
107
|
-
true
|
108
|
-
end
|
109
|
-
|
110
|
-
# Returns true, since this connection adapter supports migrations.
|
111
|
-
def supports_migrations? #:nodoc:
|
112
|
-
true
|
113
|
-
end
|
114
|
-
|
115
|
-
# Returns true.
|
116
|
-
def supports_primary_key? #:nodoc:
|
117
|
-
true
|
118
|
-
end
|
119
|
-
|
120
|
-
# Returns true.
|
121
|
-
def supports_explain?
|
122
|
-
true
|
123
|
-
end
|
124
|
-
|
125
|
-
def requires_reloading?
|
126
|
-
true
|
127
|
-
end
|
128
|
-
|
129
|
-
# Returns true if SQLite version is '3.1.6' or greater, false otherwise.
|
130
|
-
def supports_add_column?
|
131
|
-
sqlite_version >= '3.1.6'
|
132
|
-
end
|
133
|
-
|
134
|
-
# Disconnects from the database if already connected. Otherwise, this
|
135
|
-
# method does nothing.
|
136
|
-
def disconnect!
|
137
|
-
super
|
138
|
-
clear_cache!
|
139
|
-
@connection.close rescue nil
|
140
|
-
end
|
141
|
-
|
142
|
-
# Clears the prepared statements cache.
|
143
|
-
def clear_cache!
|
144
|
-
@statements.clear
|
145
|
-
end
|
146
|
-
|
147
|
-
# Returns true if SQLite version is '3.2.6' or greater, false otherwise.
|
148
|
-
def supports_count_distinct? #:nodoc:
|
149
|
-
sqlite_version >= '3.2.6'
|
150
|
-
end
|
151
|
-
|
152
|
-
# Returns true if SQLite version is '3.1.0' or greater, false otherwise.
|
153
|
-
def supports_autoincrement? #:nodoc:
|
154
|
-
sqlite_version >= '3.1.0'
|
155
|
-
end
|
156
|
-
|
157
|
-
def supports_index_sort_order?
|
158
|
-
sqlite_version >= '3.3.0'
|
159
|
-
end
|
160
|
-
|
161
|
-
def native_database_types #:nodoc:
|
162
|
-
{
|
163
|
-
:primary_key => default_primary_key_type,
|
164
|
-
:string => { :name => "varchar", :limit => 255 },
|
165
|
-
:text => { :name => "text" },
|
166
|
-
:integer => { :name => "integer" },
|
167
|
-
:float => { :name => "float" },
|
168
|
-
:decimal => { :name => "decimal" },
|
169
|
-
:datetime => { :name => "datetime" },
|
170
|
-
:timestamp => { :name => "datetime" },
|
171
|
-
:time => { :name => "time" },
|
172
|
-
:date => { :name => "date" },
|
173
|
-
:binary => { :name => "blob" },
|
174
|
-
:boolean => { :name => "boolean" }
|
175
|
-
}
|
176
|
-
end
|
177
|
-
|
178
|
-
|
179
|
-
# QUOTING ==================================================
|
180
|
-
|
181
|
-
def quote_string(s) #:nodoc:
|
182
|
-
@connection.class.quote(s)
|
183
|
-
end
|
184
|
-
|
185
|
-
def quote_column_name(name) #:nodoc:
|
186
|
-
%Q("#{name.to_s.gsub('"', '""')}")
|
187
|
-
end
|
188
|
-
|
189
|
-
# Quote date/time values for use in SQL input. Includes microseconds
|
190
|
-
# if the value is a Time responding to usec.
|
191
|
-
def quoted_date(value) #:nodoc:
|
192
|
-
if value.respond_to?(:usec)
|
193
|
-
"#{super}.#{sprintf("%06d", value.usec)}"
|
194
|
-
else
|
195
|
-
super
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
if "<3".encoding_aware?
|
200
|
-
def type_cast(value, column) # :nodoc:
|
201
|
-
return value.to_f if BigDecimal === value
|
202
|
-
return super unless String === value
|
203
|
-
return super unless column && value
|
204
|
-
|
205
|
-
value = super
|
206
|
-
if column.type == :string && value.encoding == Encoding::ASCII_8BIT
|
207
|
-
logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
|
208
|
-
value = value.encode Encoding::UTF_8
|
209
|
-
end
|
210
|
-
value
|
211
|
-
end
|
212
|
-
else
|
213
|
-
def type_cast(value, column) # :nodoc:
|
214
|
-
return super unless BigDecimal === value
|
215
|
-
|
216
|
-
value.to_f
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
# DATABASE STATEMENTS ======================================
|
221
|
-
|
222
|
-
def explain(arel, binds = [])
|
223
|
-
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
224
|
-
ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
|
225
|
-
end
|
226
|
-
|
227
|
-
class ExplainPrettyPrinter
|
228
|
-
# Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
|
229
|
-
# the output of the SQLite shell:
|
230
|
-
#
|
231
|
-
# 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
|
232
|
-
# 0|1|1|SCAN TABLE posts (~100000 rows)
|
233
|
-
#
|
234
|
-
def pp(result) # :nodoc:
|
235
|
-
result.rows.map do |row|
|
236
|
-
row.join('|')
|
237
|
-
end.join("\n") + "\n"
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
def exec_query(sql, name = nil, binds = [])
|
242
|
-
log(sql, name, binds) do
|
243
|
-
|
244
|
-
# Don't cache statements without bind values
|
245
|
-
if binds.empty?
|
246
|
-
stmt = @connection.prepare(sql)
|
247
|
-
cols = stmt.columns
|
248
|
-
records = stmt.to_a
|
249
|
-
stmt.close
|
250
|
-
stmt = records
|
251
|
-
else
|
252
|
-
cache = @statements[sql] ||= {
|
253
|
-
:stmt => @connection.prepare(sql)
|
254
|
-
}
|
255
|
-
stmt = cache[:stmt]
|
256
|
-
cols = cache[:cols] ||= stmt.columns
|
257
|
-
stmt.reset!
|
258
|
-
stmt.bind_params binds.map { |col, val|
|
259
|
-
type_cast(val, col)
|
260
|
-
}
|
261
|
-
end
|
262
|
-
|
263
|
-
ActiveRecord::Result.new(cols, stmt.to_a)
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
def exec_delete(sql, name = 'SQL', binds = [])
|
268
|
-
exec_query(sql, name, binds)
|
269
|
-
@connection.changes
|
270
|
-
end
|
271
|
-
alias :exec_update :exec_delete
|
272
|
-
|
273
|
-
def last_inserted_id(result)
|
274
|
-
@connection.last_insert_row_id
|
275
|
-
end
|
276
|
-
|
277
|
-
def execute(sql, name = nil) #:nodoc:
|
278
|
-
log(sql, name) { @connection.execute(sql) }
|
279
|
-
end
|
280
|
-
|
281
|
-
def update_sql(sql, name = nil) #:nodoc:
|
282
|
-
super
|
283
|
-
@connection.changes
|
284
|
-
end
|
285
|
-
|
286
|
-
def delete_sql(sql, name = nil) #:nodoc:
|
287
|
-
sql += " WHERE 1=1" unless sql =~ /WHERE/i
|
288
|
-
super sql, name
|
289
|
-
end
|
290
|
-
|
291
|
-
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
292
|
-
super
|
293
|
-
id_value || @connection.last_insert_row_id
|
294
|
-
end
|
295
|
-
alias :create :insert_sql
|
296
|
-
|
297
|
-
def select_rows(sql, name = nil)
|
298
|
-
exec_query(sql, name).rows
|
299
|
-
end
|
300
|
-
|
301
|
-
def create_savepoint
|
302
|
-
execute("SAVEPOINT #{current_savepoint_name}")
|
303
|
-
end
|
304
|
-
|
305
|
-
def rollback_to_savepoint
|
306
|
-
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
|
307
|
-
end
|
308
|
-
|
309
|
-
def release_savepoint
|
310
|
-
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
311
|
-
end
|
312
|
-
|
313
|
-
def begin_db_transaction #:nodoc:
|
314
|
-
log('begin transaction',nil) { @connection.transaction }
|
315
|
-
end
|
316
|
-
|
317
|
-
def commit_db_transaction #:nodoc:
|
318
|
-
log('commit transaction',nil) { @connection.commit }
|
319
|
-
end
|
320
|
-
|
321
|
-
def rollback_db_transaction #:nodoc:
|
322
|
-
log('rollback transaction',nil) { @connection.rollback }
|
323
|
-
end
|
324
|
-
|
325
|
-
# SCHEMA STATEMENTS ========================================
|
326
|
-
|
327
|
-
def tables(name = 'SCHEMA', table_name = nil) #:nodoc:
|
328
|
-
sql = <<-SQL
|
329
|
-
SELECT name
|
330
|
-
FROM sqlite_master
|
331
|
-
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
332
|
-
SQL
|
333
|
-
sql << " AND name = #{quote_table_name(table_name)}" if table_name
|
334
|
-
|
335
|
-
exec_query(sql, name).map do |row|
|
336
|
-
row['name']
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def table_exists?(name)
|
341
|
-
name && tables('SCHEMA', name).any?
|
342
|
-
end
|
343
|
-
|
344
|
-
# Returns an array of +SQLiteColumn+ objects for the table specified by +table_name+.
|
345
|
-
def columns(table_name, name = nil) #:nodoc:
|
346
|
-
table_structure(table_name).map do |field|
|
347
|
-
case field["dflt_value"]
|
348
|
-
when /^null$/i
|
349
|
-
field["dflt_value"] = nil
|
350
|
-
when /^'(.*)'$/
|
351
|
-
field["dflt_value"] = $1.gsub(/''/, "'")
|
352
|
-
when /^"(.*)"$/
|
353
|
-
field["dflt_value"] = $1.gsub(/""/, '"')
|
354
|
-
end
|
355
|
-
|
356
|
-
SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
|
-
# Returns an array of indexes for the given table.
|
361
|
-
def indexes(table_name, name = nil) #:nodoc:
|
362
|
-
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
|
363
|
-
IndexDefinition.new(
|
364
|
-
table_name,
|
365
|
-
row['name'],
|
366
|
-
row['unique'] != 0,
|
367
|
-
exec_query("PRAGMA index_info('#{row['name']}')", 'SCHEMA').map { |col|
|
368
|
-
col['name']
|
369
|
-
})
|
370
|
-
end
|
371
|
-
end
|
372
|
-
|
373
|
-
def primary_key(table_name) #:nodoc:
|
374
|
-
column = table_structure(table_name).find { |field|
|
375
|
-
field['pk'] == 1
|
376
|
-
}
|
377
|
-
column && column['name']
|
378
|
-
end
|
379
|
-
|
380
|
-
def remove_index!(table_name, index_name) #:nodoc:
|
381
|
-
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
382
|
-
end
|
383
|
-
|
384
|
-
# Renames a table.
|
385
|
-
#
|
386
|
-
# Example:
|
387
|
-
# rename_table('octopuses', 'octopi')
|
388
|
-
def rename_table(name, new_name)
|
389
|
-
exec_query "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
|
390
|
-
end
|
391
|
-
|
392
|
-
# See: http://www.sqlite.org/lang_altertable.html
|
393
|
-
# SQLite has an additional restriction on the ALTER TABLE statement
|
394
|
-
def valid_alter_table_options( type, options)
|
395
|
-
type.to_sym != :primary_key
|
396
|
-
end
|
397
|
-
|
398
|
-
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
399
|
-
if supports_add_column? && valid_alter_table_options( type, options )
|
400
|
-
super(table_name, column_name, type, options)
|
401
|
-
else
|
402
|
-
alter_table(table_name) do |definition|
|
403
|
-
definition.column(column_name, type, options)
|
404
|
-
end
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
def remove_column(table_name, *column_names) #:nodoc:
|
409
|
-
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
|
410
|
-
|
411
|
-
if column_names.flatten!
|
412
|
-
message = 'Passing array to remove_columns is deprecated, please use ' +
|
413
|
-
'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
|
414
|
-
ActiveSupport::Deprecation.warn message, caller
|
415
|
-
end
|
416
|
-
|
417
|
-
column_names.each do |column_name|
|
418
|
-
alter_table(table_name) do |definition|
|
419
|
-
definition.columns.delete(definition[column_name])
|
420
|
-
end
|
421
|
-
end
|
422
|
-
end
|
423
|
-
alias :remove_columns :remove_column
|
424
|
-
|
425
|
-
def change_column_default(table_name, column_name, default) #:nodoc:
|
426
|
-
alter_table(table_name) do |definition|
|
427
|
-
definition[column_name].default = default
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
432
|
-
unless null || default.nil?
|
433
|
-
exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
434
|
-
end
|
435
|
-
alter_table(table_name) do |definition|
|
436
|
-
definition[column_name].null = null
|
437
|
-
end
|
438
|
-
end
|
439
|
-
|
440
|
-
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
441
|
-
alter_table(table_name) do |definition|
|
442
|
-
include_default = options_include_default?(options)
|
443
|
-
definition[column_name].instance_eval do
|
444
|
-
self.type = type
|
445
|
-
self.limit = options[:limit] if options.include?(:limit)
|
446
|
-
self.default = options[:default] if include_default
|
447
|
-
self.null = options[:null] if options.include?(:null)
|
448
|
-
self.precision = options[:precision] if options.include?(:precision)
|
449
|
-
self.scale = options[:scale] if options.include?(:scale)
|
450
|
-
end
|
451
|
-
end
|
452
|
-
end
|
453
|
-
|
454
|
-
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
455
|
-
unless columns(table_name).detect{|c| c.name == column_name.to_s }
|
456
|
-
raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
|
457
|
-
end
|
458
|
-
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
|
459
|
-
end
|
460
|
-
|
461
|
-
def empty_insert_statement_value
|
462
|
-
"VALUES(NULL)"
|
463
|
-
end
|
464
|
-
|
465
|
-
protected
|
466
|
-
def select(sql, name = nil, binds = []) #:nodoc:
|
467
|
-
exec_query(sql, name, binds).to_a
|
468
|
-
end
|
469
|
-
|
470
|
-
def table_structure(table_name)
|
471
|
-
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
|
472
|
-
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
473
|
-
structure
|
474
|
-
end
|
475
|
-
|
476
|
-
def alter_table(table_name, options = {}) #:nodoc:
|
477
|
-
altered_table_name = "altered_#{table_name}"
|
478
|
-
caller = lambda {|definition| yield definition if block_given?}
|
479
|
-
|
480
|
-
transaction do
|
481
|
-
move_table(table_name, altered_table_name,
|
482
|
-
options.merge(:temporary => true))
|
483
|
-
move_table(altered_table_name, table_name, &caller)
|
484
|
-
end
|
485
|
-
end
|
486
|
-
|
487
|
-
def move_table(from, to, options = {}, &block) #:nodoc:
|
488
|
-
copy_table(from, to, options, &block)
|
489
|
-
drop_table(from)
|
490
|
-
end
|
491
|
-
|
492
|
-
def copy_table(from, to, options = {}) #:nodoc:
|
493
|
-
from_primary_key = primary_key(from)
|
494
|
-
options[:primary_key] = from_primary_key if from_primary_key != 'id'
|
495
|
-
unless options[:primary_key]
|
496
|
-
options[:id] = !columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == from_primary_key
|
497
|
-
end
|
498
|
-
create_table(to, options) do |definition|
|
499
|
-
@definition = definition
|
500
|
-
columns(from).each do |column|
|
501
|
-
column_name = options[:rename] ?
|
502
|
-
(options[:rename][column.name] ||
|
503
|
-
options[:rename][column.name.to_sym] ||
|
504
|
-
column.name) : column.name
|
505
|
-
|
506
|
-
@definition.column(column_name, column.type,
|
507
|
-
:limit => column.limit, :default => column.default,
|
508
|
-
:precision => column.precision, :scale => column.scale,
|
509
|
-
:null => column.null)
|
510
|
-
end
|
511
|
-
@definition.primary_key(from_primary_key) if from_primary_key
|
512
|
-
yield @definition if block_given?
|
513
|
-
end
|
514
|
-
|
515
|
-
copy_table_indexes(from, to, options[:rename] || {})
|
516
|
-
copy_table_contents(from, to,
|
517
|
-
@definition.columns.map {|column| column.name},
|
518
|
-
options[:rename] || {})
|
519
|
-
end
|
520
|
-
|
521
|
-
def copy_table_indexes(from, to, rename = {}) #:nodoc:
|
522
|
-
indexes(from).each do |index|
|
523
|
-
name = index.name
|
524
|
-
if to == "altered_#{from}"
|
525
|
-
name = "temp_#{name}"
|
526
|
-
elsif from == "altered_#{to}"
|
527
|
-
name = name[5..-1]
|
528
|
-
end
|
529
|
-
|
530
|
-
to_column_names = columns(to).map { |c| c.name }
|
531
|
-
columns = index.columns.map {|c| rename[c] || c }.select do |column|
|
532
|
-
to_column_names.include?(column)
|
533
|
-
end
|
534
|
-
|
535
|
-
unless columns.empty?
|
536
|
-
# index name can't be the same
|
537
|
-
opts = { :name => name.gsub(/(^|_)(#{from})_/, "\\1#{to}_") }
|
538
|
-
opts[:unique] = true if index.unique
|
539
|
-
add_index(to, columns, opts)
|
540
|
-
end
|
541
|
-
end
|
542
|
-
end
|
543
|
-
|
544
|
-
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
|
545
|
-
column_mappings = Hash[columns.map {|name| [name, name]}]
|
546
|
-
rename.each { |a| column_mappings[a.last] = a.first }
|
547
|
-
from_columns = columns(from).collect {|col| col.name}
|
548
|
-
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
|
549
|
-
quoted_columns = columns.map { |col| quote_column_name(col) } * ','
|
550
|
-
|
551
|
-
quoted_to = quote_table_name(to)
|
552
|
-
exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
|
553
|
-
sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
|
554
|
-
sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
|
555
|
-
sql << ')'
|
556
|
-
exec_query sql
|
557
|
-
end
|
558
|
-
end
|
559
|
-
|
560
|
-
def sqlite_version
|
561
|
-
@sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
|
562
|
-
end
|
563
|
-
|
564
|
-
def default_primary_key_type
|
565
|
-
if supports_autoincrement?
|
566
|
-
'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
|
567
|
-
else
|
568
|
-
'INTEGER PRIMARY KEY NOT NULL'
|
569
|
-
end
|
570
|
-
end
|
571
|
-
|
572
|
-
def translate_exception(exception, message)
|
573
|
-
case exception.message
|
574
|
-
when /column(s)? .* (is|are) not unique/
|
575
|
-
RecordNotUnique.new(message, exception)
|
576
|
-
else
|
577
|
-
super
|
578
|
-
end
|
579
|
-
end
|
580
|
-
|
581
|
-
end
|
582
|
-
end
|
583
|
-
end
|
@@ -1,68 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
|
3
|
-
# = Active Record Dynamic Finder Match
|
4
|
-
#
|
5
|
-
# Refer to ActiveRecord::Base documentation for Dynamic attribute-based finders for detailed info
|
6
|
-
#
|
7
|
-
class DynamicFinderMatch
|
8
|
-
def self.match(method)
|
9
|
-
finder = :first
|
10
|
-
bang = false
|
11
|
-
instantiator = nil
|
12
|
-
|
13
|
-
case method.to_s
|
14
|
-
when /^find_(all_|last_)?by_([_a-zA-Z]\w*)$/
|
15
|
-
finder = :last if $1 == 'last_'
|
16
|
-
finder = :all if $1 == 'all_'
|
17
|
-
names = $2
|
18
|
-
when /^find_by_([_a-zA-Z]\w*)\!$/
|
19
|
-
bang = true
|
20
|
-
names = $1
|
21
|
-
when /^find_or_create_by_([_a-zA-Z]\w*)\!$/
|
22
|
-
bang = true
|
23
|
-
instantiator = :create
|
24
|
-
names = $1
|
25
|
-
when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
|
26
|
-
instantiator = $1 == 'initialize' ? :new : :create
|
27
|
-
names = $2
|
28
|
-
else
|
29
|
-
return nil
|
30
|
-
end
|
31
|
-
|
32
|
-
new(finder, instantiator, bang, names.split('_and_'))
|
33
|
-
end
|
34
|
-
|
35
|
-
def initialize(finder, instantiator, bang, attribute_names)
|
36
|
-
@finder = finder
|
37
|
-
@instantiator = instantiator
|
38
|
-
@bang = bang
|
39
|
-
@attribute_names = attribute_names
|
40
|
-
end
|
41
|
-
|
42
|
-
attr_reader :finder, :attribute_names, :instantiator
|
43
|
-
|
44
|
-
def finder?
|
45
|
-
@finder && !@instantiator
|
46
|
-
end
|
47
|
-
|
48
|
-
def instantiator?
|
49
|
-
@finder == :first && @instantiator
|
50
|
-
end
|
51
|
-
|
52
|
-
def creator?
|
53
|
-
@finder == :first && @instantiator == :create
|
54
|
-
end
|
55
|
-
|
56
|
-
def bang?
|
57
|
-
@bang
|
58
|
-
end
|
59
|
-
|
60
|
-
def save_record?
|
61
|
-
@instantiator == :create
|
62
|
-
end
|
63
|
-
|
64
|
-
def save_method
|
65
|
-
bang? ? :save! : :save
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
|
3
|
-
# = Active Record Dynamic Scope Match
|
4
|
-
#
|
5
|
-
# Provides dynamic attribute-based scopes such as <tt>scoped_by_price(4.99)</tt>
|
6
|
-
# if, for example, the <tt>Product</tt> has an attribute with that name. You can
|
7
|
-
# chain more <tt>scoped_by_* </tt> methods after the other. It acts like a named
|
8
|
-
# scope except that it's dynamic.
|
9
|
-
class DynamicScopeMatch
|
10
|
-
def self.match(method)
|
11
|
-
return unless method.to_s =~ /^scoped_by_([_a-zA-Z]\w*)$/
|
12
|
-
new(true, $1 && $1.split('_and_'))
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(scope, attribute_names)
|
16
|
-
@scope = scope
|
17
|
-
@attribute_names = attribute_names
|
18
|
-
end
|
19
|
-
|
20
|
-
attr_reader :scope, :attribute_names
|
21
|
-
alias :scope? :scope
|
22
|
-
end
|
23
|
-
end
|