square-activerecord 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. data/CHANGELOG +6140 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +179 -0
  5. data/examples/simple.rb +14 -0
  6. data/lib/active_record.rb +124 -0
  7. data/lib/active_record/aggregations.rb +277 -0
  8. data/lib/active_record/association_preload.rb +430 -0
  9. data/lib/active_record/associations.rb +2307 -0
  10. data/lib/active_record/associations/association_collection.rb +572 -0
  11. data/lib/active_record/associations/association_proxy.rb +299 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +82 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +115 -0
  17. data/lib/active_record/associations/has_one_association.rb +143 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  19. data/lib/active_record/associations/through_association_scope.rb +154 -0
  20. data/lib/active_record/attribute_methods.rb +60 -0
  21. data/lib/active_record/attribute_methods/before_type_cast.rb +30 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +56 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +145 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +64 -0
  27. data/lib/active_record/attribute_methods/write.rb +43 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1904 -0
  30. data/lib/active_record/callbacks.rb +284 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +364 -0
  32. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  33. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +333 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +73 -0
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +539 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +217 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +657 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1031 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -0
  43. data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
  44. data/lib/active_record/counter_cache.rb +115 -0
  45. data/lib/active_record/dynamic_finder_match.rb +56 -0
  46. data/lib/active_record/dynamic_scope_match.rb +23 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1006 -0
  49. data/lib/active_record/locale/en.yml +40 -0
  50. data/lib/active_record/locking/optimistic.rb +172 -0
  51. data/lib/active_record/locking/pessimistic.rb +55 -0
  52. data/lib/active_record/log_subscriber.rb +48 -0
  53. data/lib/active_record/migration.rb +617 -0
  54. data/lib/active_record/named_scope.rb +138 -0
  55. data/lib/active_record/nested_attributes.rb +419 -0
  56. data/lib/active_record/observer.rb +125 -0
  57. data/lib/active_record/persistence.rb +290 -0
  58. data/lib/active_record/query_cache.rb +36 -0
  59. data/lib/active_record/railtie.rb +91 -0
  60. data/lib/active_record/railties/controller_runtime.rb +38 -0
  61. data/lib/active_record/railties/databases.rake +512 -0
  62. data/lib/active_record/reflection.rb +411 -0
  63. data/lib/active_record/relation.rb +394 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +295 -0
  66. data/lib/active_record/relation/finder_methods.rb +363 -0
  67. data/lib/active_record/relation/predicate_builder.rb +48 -0
  68. data/lib/active_record/relation/query_methods.rb +303 -0
  69. data/lib/active_record/relation/spawn_methods.rb +132 -0
  70. data/lib/active_record/schema.rb +59 -0
  71. data/lib/active_record/schema_dumper.rb +195 -0
  72. data/lib/active_record/serialization.rb +60 -0
  73. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  74. data/lib/active_record/session_store.rb +340 -0
  75. data/lib/active_record/test_case.rb +67 -0
  76. data/lib/active_record/timestamp.rb +88 -0
  77. data/lib/active_record/transactions.rb +359 -0
  78. data/lib/active_record/validations.rb +84 -0
  79. data/lib/active_record/validations/associated.rb +48 -0
  80. data/lib/active_record/validations/uniqueness.rb +190 -0
  81. data/lib/active_record/version.rb +10 -0
  82. data/lib/rails/generators/active_record.rb +19 -0
  83. data/lib/rails/generators/active_record/migration.rb +15 -0
  84. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  85. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  86. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  87. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  88. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  89. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  90. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  91. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  92. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  93. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  94. metadata +223 -0
@@ -0,0 +1,539 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ module SchemaStatements
6
+ # Returns a Hash of mappings from the abstract data types to the native
7
+ # database types. See TableDefinition#column for details on the recognized
8
+ # abstract data types.
9
+ def native_database_types
10
+ {}
11
+ end
12
+
13
+ # Truncates a table alias according to the limits of the current adapter.
14
+ def table_alias_for(table_name)
15
+ table_name[0..table_alias_length-1].gsub(/\./, '_')
16
+ end
17
+
18
+ # def tables(name = nil) end
19
+
20
+ def table_exists?(table_name)
21
+ tables.include?(table_name.to_s)
22
+ end
23
+
24
+ # Returns an array of indexes for the given table.
25
+ # def indexes(table_name, name = nil) end
26
+
27
+ # Checks to see if an index exists on a table for a given index definition
28
+ #
29
+ # === Examples
30
+ # # Check an index exists
31
+ # index_exists?(:suppliers, :company_id)
32
+ #
33
+ # # Check an index on multiple columns exists
34
+ # index_exists?(:suppliers, [:company_id, :company_type])
35
+ #
36
+ # # Check a unique index exists
37
+ # index_exists?(:suppliers, :company_id, :unique => true)
38
+ #
39
+ # # Check an index with a custom name exists
40
+ # index_exists?(:suppliers, :company_id, :name => "idx_company_id"
41
+ def index_exists?(table_name, column_name, options = {})
42
+ column_names = Array.wrap(column_name)
43
+ index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
44
+ if options[:unique]
45
+ indexes(table_name).any?{ |i| i.unique && i.name == index_name }
46
+ else
47
+ indexes(table_name).any?{ |i| i.name == index_name }
48
+ end
49
+ end
50
+
51
+ # Returns an array of Column objects for the table specified by +table_name+.
52
+ # See the concrete implementation for details on the expected parameter values.
53
+ def columns(table_name, name = nil) end
54
+
55
+ # Checks to see if a column exists in a given table.
56
+ #
57
+ # === Examples
58
+ # # Check a column exists
59
+ # column_exists?(:suppliers, :name)
60
+ #
61
+ # # Check a column exists of a particular type
62
+ # column_exists?(:suppliers, :name, :string)
63
+ #
64
+ # # Check a column exists with a specific definition
65
+ # column_exists?(:suppliers, :name, :string, :limit => 100)
66
+ def column_exists?(table_name, column_name, type = nil, options = {})
67
+ columns(table_name).any?{ |c| c.name == column_name.to_s &&
68
+ (!type || c.type == type) &&
69
+ (!options[:limit] || c.limit == options[:limit]) &&
70
+ (!options[:precision] || c.precision == options[:precision]) &&
71
+ (!options[:scale] || c.scale == options[:scale]) }
72
+ end
73
+
74
+ # Creates a new table with the name +table_name+. +table_name+ may either
75
+ # be a String or a Symbol.
76
+ #
77
+ # There are two ways to work with +create_table+. You can use the block
78
+ # form or the regular form, like this:
79
+ #
80
+ # === Block form
81
+ # # create_table() passes a TableDefinition object to the block.
82
+ # # This form will not only create the table, but also columns for the
83
+ # # table.
84
+ #
85
+ # create_table(:suppliers) do |t|
86
+ # t.column :name, :string, :limit => 60
87
+ # # Other fields here
88
+ # end
89
+ #
90
+ # === Block form, with shorthand
91
+ # # You can also use the column types as method calls, rather than calling the column method.
92
+ # create_table(:suppliers) do |t|
93
+ # t.string :name, :limit => 60
94
+ # # Other fields here
95
+ # end
96
+ #
97
+ # === Regular form
98
+ # # Creates a table called 'suppliers' with no columns.
99
+ # create_table(:suppliers)
100
+ # # Add a column to 'suppliers'.
101
+ # add_column(:suppliers, :name, :string, {:limit => 60})
102
+ #
103
+ # The +options+ hash can include the following keys:
104
+ # [<tt>:id</tt>]
105
+ # Whether to automatically add a primary key column. Defaults to true.
106
+ # Join tables for +has_and_belongs_to_many+ should set it to false.
107
+ # [<tt>:primary_key</tt>]
108
+ # The name of the primary key, if one is to be added automatically.
109
+ # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
110
+ #
111
+ # Also note that this just sets the primary key in the table. You additionally
112
+ # need to configure the primary key in the model via the +set_primary_key+ macro.
113
+ # Models do NOT auto-detect the primary key from their table definition.
114
+ #
115
+ # [<tt>:options</tt>]
116
+ # Any extra options you want appended to the table definition.
117
+ # [<tt>:temporary</tt>]
118
+ # Make a temporary table.
119
+ # [<tt>:force</tt>]
120
+ # Set to true to drop the table before creating it.
121
+ # Defaults to false.
122
+ #
123
+ # ===== Examples
124
+ # ====== Add a backend specific option to the generated SQL (MySQL)
125
+ # create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
126
+ # generates:
127
+ # CREATE TABLE suppliers (
128
+ # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
129
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
130
+ #
131
+ # ====== Rename the primary key column
132
+ # create_table(:objects, :primary_key => 'guid') do |t|
133
+ # t.column :name, :string, :limit => 80
134
+ # end
135
+ # generates:
136
+ # CREATE TABLE objects (
137
+ # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
138
+ # name varchar(80)
139
+ # )
140
+ #
141
+ # ====== Do not add a primary key column
142
+ # create_table(:categories_suppliers, :id => false) do |t|
143
+ # t.column :category_id, :integer
144
+ # t.column :supplier_id, :integer
145
+ # end
146
+ # generates:
147
+ # CREATE TABLE categories_suppliers (
148
+ # category_id int,
149
+ # supplier_id int
150
+ # )
151
+ #
152
+ # See also TableDefinition#column for details on how to create columns.
153
+ def create_table(table_name, options = {})
154
+ table_definition = TableDefinition.new(self)
155
+ table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
156
+
157
+ yield table_definition if block_given?
158
+
159
+ if options[:force] && table_exists?(table_name)
160
+ drop_table(table_name, options)
161
+ end
162
+
163
+ create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
164
+ create_sql << "#{quote_table_name(table_name)} ("
165
+ create_sql << table_definition.to_sql
166
+ create_sql << ") #{options[:options]}"
167
+ execute create_sql
168
+ end
169
+
170
+ # A block for changing columns in +table+.
171
+ #
172
+ # === Example
173
+ # # change_table() yields a Table instance
174
+ # change_table(:suppliers) do |t|
175
+ # t.column :name, :string, :limit => 60
176
+ # # Other column alterations here
177
+ # end
178
+ #
179
+ # ===== Examples
180
+ # ====== Add a column
181
+ # change_table(:suppliers) do |t|
182
+ # t.column :name, :string, :limit => 60
183
+ # end
184
+ #
185
+ # ====== Add 2 integer columns
186
+ # change_table(:suppliers) do |t|
187
+ # t.integer :width, :height, :null => false, :default => 0
188
+ # end
189
+ #
190
+ # ====== Add created_at/updated_at columns
191
+ # change_table(:suppliers) do |t|
192
+ # t.timestamps
193
+ # end
194
+ #
195
+ # ====== Add a foreign key column
196
+ # change_table(:suppliers) do |t|
197
+ # t.references :company
198
+ # end
199
+ #
200
+ # Creates a <tt>company_id(integer)</tt> column
201
+ #
202
+ # ====== Add a polymorphic foreign key column
203
+ # change_table(:suppliers) do |t|
204
+ # t.belongs_to :company, :polymorphic => true
205
+ # end
206
+ #
207
+ # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns
208
+ #
209
+ # ====== Remove a column
210
+ # change_table(:suppliers) do |t|
211
+ # t.remove :company
212
+ # end
213
+ #
214
+ # ====== Remove several columns
215
+ # change_table(:suppliers) do |t|
216
+ # t.remove :company_id
217
+ # t.remove :width, :height
218
+ # end
219
+ #
220
+ # ====== Remove an index
221
+ # change_table(:suppliers) do |t|
222
+ # t.remove_index :company_id
223
+ # end
224
+ #
225
+ # See also Table for details on
226
+ # all of the various column transformation
227
+ def change_table(table_name)
228
+ yield Table.new(table_name, self)
229
+ end
230
+
231
+ # Renames a table.
232
+ # ===== Example
233
+ # rename_table('octopuses', 'octopi')
234
+ def rename_table(table_name, new_name)
235
+ raise NotImplementedError, "rename_table is not implemented"
236
+ end
237
+
238
+ # Drops a table from the database.
239
+ def drop_table(table_name, options = {})
240
+ execute "DROP TABLE #{quote_table_name(table_name)}"
241
+ end
242
+
243
+ # Adds a new column to the named table.
244
+ # See TableDefinition#column for details of the options you can use.
245
+ def add_column(table_name, column_name, type, options = {})
246
+ add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
247
+ add_column_options!(add_column_sql, options)
248
+ execute(add_column_sql)
249
+ end
250
+
251
+ # Removes the column(s) from the table definition.
252
+ # ===== Examples
253
+ # remove_column(:suppliers, :qualification)
254
+ # remove_columns(:suppliers, :qualification, :experience)
255
+ def remove_column(table_name, *column_names)
256
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
257
+ column_names.flatten.each do |column_name|
258
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
259
+ end
260
+ end
261
+ alias :remove_columns :remove_column
262
+
263
+ # Changes the column's definition according to the new options.
264
+ # See TableDefinition#column for details of the options you can use.
265
+ # ===== Examples
266
+ # change_column(:suppliers, :name, :string, :limit => 80)
267
+ # change_column(:accounts, :description, :text)
268
+ def change_column(table_name, column_name, type, options = {})
269
+ raise NotImplementedError, "change_column is not implemented"
270
+ end
271
+
272
+ # Sets a new default value for a column. If you want to set the default
273
+ # value to +NULL+, you are out of luck. You need to
274
+ # DatabaseStatements#execute the appropriate SQL statement yourself.
275
+ # ===== Examples
276
+ # change_column_default(:suppliers, :qualification, 'new')
277
+ # change_column_default(:accounts, :authorized, 1)
278
+ def change_column_default(table_name, column_name, default)
279
+ raise NotImplementedError, "change_column_default is not implemented"
280
+ end
281
+
282
+ # Renames a column.
283
+ # ===== Example
284
+ # rename_column(:suppliers, :description, :name)
285
+ def rename_column(table_name, column_name, new_column_name)
286
+ raise NotImplementedError, "rename_column is not implemented"
287
+ end
288
+
289
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
290
+ # an Array of Symbols.
291
+ #
292
+ # The index will be named after the table and the first column name,
293
+ # unless you pass <tt>:name</tt> as an option.
294
+ #
295
+ # When creating an index on multiple columns, the first column is used as a name
296
+ # for the index. For example, when you specify an index on two columns
297
+ # [<tt>:first</tt>, <tt>:last</tt>], the DBMS creates an index for both columns as well as an
298
+ # index for the first column <tt>:first</tt>. Using just the first name for this index
299
+ # makes sense, because you will never have to create a singular index with this
300
+ # name.
301
+ #
302
+ # ===== Examples
303
+ #
304
+ # ====== Creating a simple index
305
+ # add_index(:suppliers, :name)
306
+ # generates
307
+ # CREATE INDEX suppliers_name_index ON suppliers(name)
308
+ #
309
+ # ====== Creating a unique index
310
+ # add_index(:accounts, [:branch_id, :party_id], :unique => true)
311
+ # generates
312
+ # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
313
+ #
314
+ # ====== Creating a named index
315
+ # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
316
+ # generates
317
+ # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
318
+ #
319
+ # ====== Creating an index with specific key length
320
+ # add_index(:accounts, :name, :name => 'by_name', :length => 10)
321
+ # generates
322
+ # CREATE INDEX by_name ON accounts(name(10))
323
+ #
324
+ # add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
325
+ # generates
326
+ # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
327
+ #
328
+ # Note: SQLite doesn't support index length
329
+ def add_index(table_name, column_name, options = {})
330
+ column_names = Array.wrap(column_name)
331
+ index_name = index_name(table_name, :column => column_names)
332
+
333
+ if Hash === options # legacy support, since this param was a string
334
+ index_type = options[:unique] ? "UNIQUE" : ""
335
+ index_name = options[:name].to_s if options.key?(:name)
336
+ else
337
+ index_type = options
338
+ end
339
+
340
+ if index_name.length > index_name_length
341
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
342
+ end
343
+ if index_name_exists?(table_name, index_name, false)
344
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
345
+ end
346
+ quoted_column_names = quoted_columns_for_index(column_names, options).join(", ")
347
+
348
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
349
+ end
350
+
351
+ # Remove the given index from the table.
352
+ #
353
+ # Remove the suppliers_name_index in the suppliers table.
354
+ # remove_index :suppliers, :name
355
+ # Remove the index named accounts_branch_id_index in the accounts table.
356
+ # remove_index :accounts, :column => :branch_id
357
+ # Remove the index named accounts_branch_id_party_id_index in the accounts table.
358
+ # remove_index :accounts, :column => [:branch_id, :party_id]
359
+ # Remove the index named by_branch_party in the accounts table.
360
+ # remove_index :accounts, :name => :by_branch_party
361
+ def remove_index(table_name, options = {})
362
+ index_name = index_name(table_name, options)
363
+ unless index_name_exists?(table_name, index_name, true)
364
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
365
+ end
366
+ remove_index!(table_name, index_name)
367
+ end
368
+
369
+ def remove_index!(table_name, index_name) #:nodoc:
370
+ execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
371
+ end
372
+
373
+ # Rename an index.
374
+ #
375
+ # Rename the index_people_on_last_name index to index_users_on_last_name
376
+ # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
377
+ def rename_index(table_name, old_name, new_name)
378
+ # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
379
+ old_index_def = indexes(table_name).detect { |i| i.name == old_name }
380
+ return unless old_index_def
381
+ remove_index(table_name, :name => old_name)
382
+ add_index(table_name, old_index_def.columns, :name => new_name, :unique => old_index_def.unique)
383
+ end
384
+
385
+ def index_name(table_name, options) #:nodoc:
386
+ if Hash === options # legacy support
387
+ if options[:column]
388
+ "index_#{table_name}_on_#{Array.wrap(options[:column]) * '_and_'}"
389
+ elsif options[:name]
390
+ options[:name]
391
+ else
392
+ raise ArgumentError, "You must specify the index name"
393
+ end
394
+ else
395
+ index_name(table_name, :column => options)
396
+ end
397
+ end
398
+
399
+ # Verify the existence of an index with a given name.
400
+ #
401
+ # The default argument is returned if the underlying implementation does not define the indexes method,
402
+ # as there's no way to determine the correct answer in that case.
403
+ def index_name_exists?(table_name, index_name, default)
404
+ return default unless respond_to?(:indexes)
405
+ index_name = index_name.to_s
406
+ indexes(table_name).detect { |i| i.name == index_name }
407
+ end
408
+
409
+ # Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
410
+ # entire structure of the database.
411
+ def structure_dump
412
+ end
413
+
414
+ def dump_schema_information #:nodoc:
415
+ sm_table = ActiveRecord::Migrator.schema_migrations_table_name
416
+ migrated = select_values("SELECT version FROM #{sm_table}")
417
+ migrated.map { |v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}');" }.join("\n\n")
418
+ end
419
+
420
+ # Should not be called normally, but this operation is non-destructive.
421
+ # The migrations module handles this automatically.
422
+ def initialize_schema_migrations_table
423
+ sm_table = ActiveRecord::Migrator.schema_migrations_table_name
424
+
425
+ unless table_exists?(sm_table)
426
+ create_table(sm_table, :id => false) do |schema_migrations_table|
427
+ schema_migrations_table.column :version, :string, :null => false
428
+ end
429
+ add_index sm_table, :version, :unique => true,
430
+ :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
431
+
432
+ # Backwards-compatibility: if we find schema_info, assume we've
433
+ # migrated up to that point:
434
+ si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
435
+
436
+ if table_exists?(si_table)
437
+
438
+ old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
439
+ assume_migrated_upto_version(old_version)
440
+ drop_table(si_table)
441
+ end
442
+ end
443
+ end
444
+
445
+ def assume_migrated_upto_version(version, migrations_path = ActiveRecord::Migrator.migrations_path)
446
+ version = version.to_i
447
+ sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
448
+
449
+ migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
450
+ versions = Dir["#{migrations_path}/[0-9]*_*.rb"].map do |filename|
451
+ filename.split('/').last.split('_').first.to_i
452
+ end
453
+
454
+ unless migrated.include?(version)
455
+ execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
456
+ end
457
+
458
+ inserted = Set.new
459
+ (versions - migrated).each do |v|
460
+ if inserted.include?(v)
461
+ raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
462
+ elsif v < version
463
+ execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
464
+ inserted << v
465
+ end
466
+ end
467
+ end
468
+
469
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
470
+ if native = native_database_types[type]
471
+ column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
472
+
473
+ if type == :decimal # ignore limit, use precision and scale
474
+ scale ||= native[:scale]
475
+
476
+ if precision ||= native[:precision]
477
+ if scale
478
+ column_type_sql << "(#{precision},#{scale})"
479
+ else
480
+ column_type_sql << "(#{precision})"
481
+ end
482
+ elsif scale
483
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
484
+ end
485
+
486
+ elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
487
+ column_type_sql << "(#{limit})"
488
+ end
489
+
490
+ column_type_sql
491
+ else
492
+ type
493
+ end
494
+ end
495
+
496
+ def add_column_options!(sql, options) #:nodoc:
497
+ sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
498
+ # must explicitly check for :null to allow change_column to work on migrations
499
+ if options[:null] == false
500
+ sql << " NOT NULL"
501
+ end
502
+ end
503
+
504
+ # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
505
+ # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax.
506
+ #
507
+ # distinct("posts.id", "posts.created_at desc")
508
+ def distinct(columns, order_by)
509
+ "DISTINCT #{columns}"
510
+ end
511
+
512
+ # Adds timestamps (created_at and updated_at) columns to the named table.
513
+ # ===== Examples
514
+ # add_timestamps(:suppliers)
515
+ def add_timestamps(table_name)
516
+ add_column table_name, :created_at, :datetime
517
+ add_column table_name, :updated_at, :datetime
518
+ end
519
+
520
+ # Removes the timestamp columns (created_at and updated_at) from the table definition.
521
+ # ===== Examples
522
+ # remove_timestamps(:suppliers)
523
+ def remove_timestamps(table_name)
524
+ remove_column table_name, :updated_at
525
+ remove_column table_name, :created_at
526
+ end
527
+
528
+ protected
529
+ # Overridden by the mysql adapter for supporting index lengths
530
+ def quoted_columns_for_index(column_names, options = {})
531
+ column_names.map {|name| quote_column_name(name) }
532
+ end
533
+
534
+ def options_include_default?(options)
535
+ options.include?(:default) && !(options[:null] == false && options[:default].nil?)
536
+ end
537
+ end
538
+ end
539
+ end