activerecord 1.14.4 → 1.15.0

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

Potentially problematic release.


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

Files changed (159) hide show
  1. data/CHANGELOG +400 -1
  2. data/README +2 -2
  3. data/RUNNING_UNIT_TESTS +21 -3
  4. data/Rakefile +55 -10
  5. data/lib/active_record.rb +10 -4
  6. data/lib/active_record/acts/list.rb +15 -4
  7. data/lib/active_record/acts/nested_set.rb +11 -12
  8. data/lib/active_record/acts/tree.rb +13 -14
  9. data/lib/active_record/aggregations.rb +46 -22
  10. data/lib/active_record/associations.rb +213 -162
  11. data/lib/active_record/associations/association_collection.rb +45 -15
  12. data/lib/active_record/associations/association_proxy.rb +32 -13
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +18 -18
  14. data/lib/active_record/associations/has_many_association.rb +37 -17
  15. data/lib/active_record/associations/has_many_through_association.rb +120 -30
  16. data/lib/active_record/associations/has_one_association.rb +1 -1
  17. data/lib/active_record/attribute_methods.rb +75 -0
  18. data/lib/active_record/base.rb +282 -203
  19. data/lib/active_record/calculations.rb +95 -54
  20. data/lib/active_record/callbacks.rb +13 -24
  21. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +12 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb.rej +21 -0
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -4
  24. data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -9
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +121 -37
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -23
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
  28. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -11
  29. data/lib/active_record/connection_adapters/firebird_adapter.rb +364 -50
  30. data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
  31. data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -33
  32. data/lib/active_record/connection_adapters/openbase_adapter.rb +4 -3
  33. data/lib/active_record/connection_adapters/oracle_adapter.rb +151 -127
  34. data/lib/active_record/connection_adapters/postgresql_adapter.rb +125 -48
  35. data/lib/active_record/connection_adapters/sqlite_adapter.rb +38 -10
  36. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +183 -155
  37. data/lib/active_record/connection_adapters/sybase_adapter.rb +190 -212
  38. data/lib/active_record/deprecated_associations.rb +24 -10
  39. data/lib/active_record/deprecated_finders.rb +4 -1
  40. data/lib/active_record/fixtures.rb +37 -23
  41. data/lib/active_record/locking/optimistic.rb +106 -0
  42. data/lib/active_record/locking/pessimistic.rb +77 -0
  43. data/lib/active_record/migration.rb +8 -5
  44. data/lib/active_record/observer.rb +73 -34
  45. data/lib/active_record/reflection.rb +21 -7
  46. data/lib/active_record/schema_dumper.rb +33 -5
  47. data/lib/active_record/timestamp.rb +23 -34
  48. data/lib/active_record/transactions.rb +37 -30
  49. data/lib/active_record/validations.rb +46 -30
  50. data/lib/active_record/vendor/mysql.rb +20 -5
  51. data/lib/active_record/version.rb +2 -2
  52. data/lib/active_record/wrappings.rb +1 -2
  53. data/lib/active_record/xml_serialization.rb +308 -0
  54. data/test/aaa_create_tables_test.rb +5 -1
  55. data/test/abstract_unit.rb +18 -8
  56. data/test/{active_schema_mysql.rb → active_schema_test_mysql.rb} +2 -2
  57. data/test/adapter_test.rb +9 -7
  58. data/test/adapter_test_sqlserver.rb +81 -0
  59. data/test/aggregations_test.rb +29 -0
  60. data/test/{association_callbacks_test.rb → associations/callbacks_test.rb} +10 -8
  61. data/test/{associations_cascaded_eager_loading_test.rb → associations/cascaded_eager_loading_test.rb} +35 -3
  62. data/test/{associations_go_eager_test.rb → associations/eager_test.rb} +36 -2
  63. data/test/{associations_extensions_test.rb → associations/extension_test.rb} +5 -0
  64. data/test/{associations_join_model_test.rb → associations/join_model_test.rb} +118 -8
  65. data/test/associations_test.rb +339 -45
  66. data/test/attribute_methods_test.rb +49 -0
  67. data/test/base_test.rb +321 -67
  68. data/test/calculations_test.rb +48 -10
  69. data/test/callbacks_test.rb +13 -0
  70. data/test/connection_test_firebird.rb +8 -0
  71. data/test/connections/native_db2/connection.rb +18 -17
  72. data/test/connections/native_firebird/connection.rb +19 -17
  73. data/test/connections/native_frontbase/connection.rb +27 -0
  74. data/test/connections/native_mysql/connection.rb +18 -15
  75. data/test/connections/native_openbase/connection.rb +14 -15
  76. data/test/connections/native_oracle/connection.rb +16 -12
  77. data/test/connections/native_postgresql/connection.rb +16 -17
  78. data/test/connections/native_sqlite/connection.rb +3 -6
  79. data/test/connections/native_sqlite3/connection.rb +3 -6
  80. data/test/connections/native_sqlserver/connection.rb +16 -17
  81. data/test/connections/native_sqlserver_odbc/connection.rb +18 -19
  82. data/test/connections/native_sybase/connection.rb +16 -17
  83. data/test/datatype_test_postgresql.rb +52 -0
  84. data/test/defaults_test.rb +52 -10
  85. data/test/deprecated_associations_test.rb +151 -107
  86. data/test/deprecated_finder_test.rb +83 -66
  87. data/test/empty_date_time_test.rb +25 -0
  88. data/test/finder_test.rb +118 -11
  89. data/test/fixtures/accounts.yml +6 -1
  90. data/test/fixtures/author.rb +27 -4
  91. data/test/fixtures/categorizations.yml +8 -2
  92. data/test/fixtures/category.rb +1 -2
  93. data/test/fixtures/comments.yml +0 -6
  94. data/test/fixtures/companies.yml +6 -1
  95. data/test/fixtures/company.rb +23 -1
  96. data/test/fixtures/company_in_module.rb +8 -10
  97. data/test/fixtures/customer.rb +2 -2
  98. data/test/fixtures/customers.yml +9 -0
  99. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  100. data/test/fixtures/db_definitions/db2.sql +9 -0
  101. data/test/fixtures/db_definitions/firebird.drop.sql +3 -0
  102. data/test/fixtures/db_definitions/firebird.sql +13 -1
  103. data/test/fixtures/db_definitions/frontbase.drop.sql +31 -0
  104. data/test/fixtures/db_definitions/frontbase.sql +262 -0
  105. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  107. data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
  108. data/test/fixtures/db_definitions/mysql.sql +23 -14
  109. data/test/fixtures/db_definitions/openbase.sql +13 -1
  110. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  111. data/test/fixtures/db_definitions/oracle.sql +29 -2
  112. data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
  113. data/test/fixtures/db_definitions/postgresql.sql +13 -3
  114. data/test/fixtures/db_definitions/schema.rb +29 -1
  115. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  116. data/test/fixtures/db_definitions/sqlite.sql +12 -3
  117. data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
  118. data/test/fixtures/db_definitions/sqlserver.sql +35 -0
  119. data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
  120. data/test/fixtures/db_definitions/sybase.sql +13 -4
  121. data/test/fixtures/developer.rb +12 -0
  122. data/test/fixtures/edge.rb +5 -0
  123. data/test/fixtures/edges.yml +6 -0
  124. data/test/fixtures/funny_jokes.yml +3 -7
  125. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  126. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  127. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  128. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  129. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  130. data/test/fixtures/mixin.rb +15 -0
  131. data/test/fixtures/mixins.yml +38 -0
  132. data/test/fixtures/post.rb +3 -2
  133. data/test/fixtures/project.rb +3 -1
  134. data/test/fixtures/topic.rb +6 -1
  135. data/test/fixtures/topics.yml +4 -4
  136. data/test/fixtures/vertex.rb +9 -0
  137. data/test/fixtures/vertices.yml +4 -0
  138. data/test/fixtures_test.rb +45 -0
  139. data/test/inheritance_test.rb +67 -6
  140. data/test/lifecycle_test.rb +40 -19
  141. data/test/locking_test.rb +170 -26
  142. data/test/method_scoping_test.rb +2 -2
  143. data/test/migration_test.rb +387 -110
  144. data/test/migration_test_firebird.rb +124 -0
  145. data/test/mixin_nested_set_test.rb +14 -2
  146. data/test/mixin_test.rb +56 -18
  147. data/test/modules_test.rb +8 -2
  148. data/test/multiple_db_test.rb +2 -2
  149. data/test/pk_test.rb +1 -0
  150. data/test/reflection_test.rb +8 -2
  151. data/test/schema_authorization_test_postgresql.rb +75 -0
  152. data/test/schema_dumper_test.rb +40 -4
  153. data/test/table_name_test_sqlserver.rb +23 -0
  154. data/test/threaded_connections_test.rb +19 -16
  155. data/test/transactions_test.rb +86 -72
  156. data/test/validations_test.rb +126 -56
  157. data/test/xml_serialization_test.rb +125 -0
  158. metadata +45 -11
  159. data/lib/active_record/locking.rb +0 -79
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
 
9
9
  config = config.symbolize_keys
10
10
  host = config[:host]
11
- port = config[:port] || 5432 unless host.nil?
11
+ port = config[:port] || 5432
12
12
  username = config[:username].to_s
13
13
  password = config[:password].to_s
14
14
 
@@ -46,6 +46,7 @@ module ActiveRecord
46
46
  # * <tt>:schema_search_path</tt> -- An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the :schema_order option.
47
47
  # * <tt>:encoding</tt> -- An optional client encoding that is using in a SET client_encoding TO <encoding> call on connection.
48
48
  # * <tt>:min_messages</tt> -- An optional client min messages that is using in a SET client_min_messages TO <min_messages> call on connection.
49
+ # * <tt>:allow_concurrency</tt> -- If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods.
49
50
  class PostgreSQLAdapter < AbstractAdapter
50
51
  def adapter_name
51
52
  'PostgreSQL'
@@ -54,6 +55,7 @@ module ActiveRecord
54
55
  def initialize(connection, logger, config = {})
55
56
  super(connection, logger)
56
57
  @config = config
58
+ @async = config[:allow_concurrency]
57
59
  configure_connection
58
60
  end
59
61
 
@@ -67,7 +69,7 @@ module ActiveRecord
67
69
  end
68
70
  # postgres-pr raises a NoMethodError when querying if no conn is available
69
71
  rescue PGError, NoMethodError
70
- false
72
+ false
71
73
  end
72
74
 
73
75
  # Close then reopen the connection.
@@ -78,7 +80,7 @@ module ActiveRecord
78
80
  configure_connection
79
81
  end
80
82
  end
81
-
83
+
82
84
  def disconnect!
83
85
  # Both postgres and postgres-pr respond to :close
84
86
  @connection.close rescue nil
@@ -91,6 +93,7 @@ module ActiveRecord
91
93
  :text => { :name => "text" },
92
94
  :integer => { :name => "integer" },
93
95
  :float => { :name => "float" },
96
+ :decimal => { :name => "decimal" },
94
97
  :datetime => { :name => "timestamp" },
95
98
  :timestamp => { :name => "timestamp" },
96
99
  :time => { :name => "time" },
@@ -99,11 +102,11 @@ module ActiveRecord
99
102
  :boolean => { :name => "boolean" }
100
103
  }
101
104
  end
102
-
105
+
103
106
  def supports_migrations?
104
107
  true
105
- end
106
-
108
+ end
109
+
107
110
  def table_alias_length
108
111
  63
109
112
  end
@@ -122,17 +125,12 @@ module ActiveRecord
122
125
  %("#{name}")
123
126
  end
124
127
 
125
-
126
- # DATABASE STATEMENTS ======================================
127
-
128
- def select_all(sql, name = nil) #:nodoc:
129
- select(sql, name)
128
+ def quoted_date(value)
129
+ value.strftime("%Y-%m-%d %H:%M:%S.#{sprintf("%06d", value.usec)}")
130
130
  end
131
131
 
132
- def select_one(sql, name = nil) #:nodoc:
133
- result = select(sql, name)
134
- result.first if result
135
- end
132
+
133
+ # DATABASE STATEMENTS ======================================
136
134
 
137
135
  def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
138
136
  execute(sql, name)
@@ -141,20 +139,29 @@ module ActiveRecord
141
139
  end
142
140
 
143
141
  def query(sql, name = nil) #:nodoc:
144
- log(sql, name) { @connection.query(sql) }
142
+ log(sql, name) do
143
+ if @async
144
+ @connection.async_query(sql)
145
+ else
146
+ @connection.query(sql)
147
+ end
148
+ end
145
149
  end
146
150
 
147
151
  def execute(sql, name = nil) #:nodoc:
148
- log(sql, name) { @connection.exec(sql) }
152
+ log(sql, name) do
153
+ if @async
154
+ @connection.async_exec(sql)
155
+ else
156
+ @connection.exec(sql)
157
+ end
158
+ end
149
159
  end
150
160
 
151
161
  def update(sql, name = nil) #:nodoc:
152
162
  execute(sql, name).cmdtuples
153
163
  end
154
164
 
155
- alias_method :delete, :update #:nodoc:
156
-
157
-
158
165
  def begin_db_transaction #:nodoc:
159
166
  execute "BEGIN"
160
167
  end
@@ -162,12 +169,11 @@ module ActiveRecord
162
169
  def commit_db_transaction #:nodoc:
163
170
  execute "COMMIT"
164
171
  end
165
-
172
+
166
173
  def rollback_db_transaction #:nodoc:
167
174
  execute "ROLLBACK"
168
175
  end
169
176
 
170
-
171
177
  # SCHEMA STATEMENTS ========================================
172
178
 
173
179
  # Return the list of all tables in the schema search path.
@@ -214,9 +220,9 @@ module ActiveRecord
214
220
  end
215
221
 
216
222
  def columns(table_name, name = nil) #:nodoc:
217
- column_definitions(table_name).collect do |name, type, default, notnull|
218
- Column.new(name, default_value(default), translate_field_type(type),
219
- notnull == "f")
223
+ column_definitions(table_name).collect do |name, type, default, notnull, typmod|
224
+ # typmod now unused as limit, precision, scale all handled by superclass
225
+ Column.new(name, default_value(default), translate_field_type(type), notnull == "f")
220
226
  end
221
227
  end
222
228
 
@@ -261,7 +267,7 @@ module ActiveRecord
261
267
  def pk_and_sequence_for(table)
262
268
  # First try looking for a sequence with a dependency on the
263
269
  # given table's primary key.
264
- result = execute(<<-end_sql, 'PK and serial sequence')[0]
270
+ result = query(<<-end_sql, 'PK and serial sequence')[0]
265
271
  SELECT attr.attname, name.nspname, seq.relname
266
272
  FROM pg_class seq,
267
273
  pg_attribute attr,
@@ -284,8 +290,8 @@ module ActiveRecord
284
290
  # Support the 7.x and 8.0 nextval('foo'::text) as well as
285
291
  # the 8.1+ nextval('foo'::regclass).
286
292
  # TODO: assumes sequence is in same schema as table.
287
- result = execute(<<-end_sql, 'PK and custom sequence')[0]
288
- SELECT attr.attname, name.nspname, split_part(def.adsrc, '\\\'', 2)
293
+ result = query(<<-end_sql, 'PK and custom sequence')[0]
294
+ SELECT attr.attname, name.nspname, split_part(def.adsrc, '''', 2)
289
295
  FROM pg_class t
290
296
  JOIN pg_namespace name ON (t.relnamespace = name.oid)
291
297
  JOIN pg_attribute attr ON (t.oid = attrelid)
@@ -296,8 +302,9 @@ module ActiveRecord
296
302
  AND def.adsrc ~* 'nextval'
297
303
  end_sql
298
304
  end
299
- # check for existence of . in sequence name as in public.foo_sequence. if it does not exist, join the current namespace
300
- result.last['.'] ? [result.first, result.last] : [result.first, "#{result[1]}.#{result[2]}"]
305
+ # check for existence of . in sequence name as in public.foo_sequence. if it does not exist, return unqualified sequence
306
+ # We cannot qualify unqualified sequences, as rails doesn't qualify any table access, using the search path
307
+ [result.first, result.last]
301
308
  rescue
302
309
  nil
303
310
  end
@@ -305,42 +312,107 @@ module ActiveRecord
305
312
  def rename_table(name, new_name)
306
313
  execute "ALTER TABLE #{name} RENAME TO #{new_name}"
307
314
  end
308
-
315
+
309
316
  def add_column(table_name, column_name, type, options = {})
310
- execute("ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}")
311
- execute("ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL") if options[:null] == false
312
- change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
317
+ default = options[:default]
318
+ notnull = options[:null] == false
319
+
320
+ # Add the column.
321
+ execute("ALTER TABLE #{table_name} ADD COLUMN #{column_name} #{type_to_sql(type, options[:limit])}")
322
+
323
+ # Set optional default. If not null, update nulls to the new default.
324
+ if options_include_default?(options)
325
+ change_column_default(table_name, column_name, default)
326
+ if notnull
327
+ execute("UPDATE #{table_name} SET #{column_name}=#{quote(default, options[:column])} WHERE #{column_name} IS NULL")
328
+ end
329
+ end
330
+
331
+ if notnull
332
+ execute("ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL")
333
+ end
313
334
  end
314
335
 
315
336
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
316
337
  begin
317
- execute "ALTER TABLE #{table_name} ALTER #{column_name} TYPE #{type_to_sql(type, options[:limit])}"
338
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
318
339
  rescue ActiveRecord::StatementInvalid
319
340
  # This is PG7, so we use a more arcane way of doing it.
320
341
  begin_db_transaction
321
342
  add_column(table_name, "#{column_name}_ar_tmp", type, options)
322
- execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit])})"
343
+ execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"
323
344
  remove_column(table_name, column_name)
324
345
  rename_column(table_name, "#{column_name}_ar_tmp", column_name)
325
346
  commit_db_transaction
326
347
  end
327
- change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
328
- end
348
+
349
+ if options_include_default?(options)
350
+ change_column_default(table_name, column_name, options[:default])
351
+ end
352
+ end
329
353
 
330
354
  def change_column_default(table_name, column_name, default) #:nodoc:
331
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT '#{default}'"
355
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
332
356
  end
333
-
357
+
334
358
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
335
- execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
359
+ execute "ALTER TABLE #{table_name} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
336
360
  end
337
361
 
338
362
  def remove_index(table_name, options) #:nodoc:
339
363
  execute "DROP INDEX #{index_name(table_name, options)}"
340
364
  end
341
365
 
366
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
367
+ return super unless type.to_s == 'integer'
368
+
369
+ if limit.nil? || limit == 4
370
+ 'integer'
371
+ elsif limit < 4
372
+ 'smallint'
373
+ else
374
+ 'bigint'
375
+ end
376
+ end
377
+
378
+ # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
379
+ #
380
+ # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
381
+ # requires that the ORDER BY include the distinct column.
382
+ #
383
+ # distinct("posts.id", "posts.created_at desc")
384
+ def distinct(columns, order_by)
385
+ return "DISTINCT #{columns}" if order_by.blank?
386
+
387
+ # construct a clean list of column names from the ORDER BY clause, removing
388
+ # any asc/desc modifiers
389
+ order_columns = order_by.split(',').collect { |s| s.split.first }
390
+ order_columns.delete_if &:blank?
391
+ order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
392
+
393
+ # return a DISTINCT ON() clause that's distinct on the columns we want but includes
394
+ # all the required columns for the ORDER BY to work properly
395
+ sql = "DISTINCT ON (#{columns}) #{columns}, "
396
+ sql << order_columns * ', '
397
+ end
398
+
399
+ # ORDER BY clause for the passed order option.
400
+ #
401
+ # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
402
+ # by wrapping the sql as a sub-select and ordering in that query.
403
+ def add_order_by_for_association_limiting!(sql, options)
404
+ return sql if options[:order].blank?
405
+
406
+ order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
407
+ order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
408
+ order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')
409
+
410
+ sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
411
+ end
412
+
342
413
  private
343
414
  BYTEA_COLUMN_TYPE_OID = 17
415
+ NUMERIC_COLUMN_TYPE_OID = 1700
344
416
  TIMESTAMPOID = 1114
345
417
  TIMESTAMPTZOID = 1184
346
418
 
@@ -367,12 +439,14 @@ module ActiveRecord
367
439
  hashed_row = {}
368
440
  row.each_index do |cel_index|
369
441
  column = row[cel_index]
370
-
442
+
371
443
  case res.type(cel_index)
372
444
  when BYTEA_COLUMN_TYPE_OID
373
445
  column = unescape_bytea(column)
374
446
  when TIMESTAMPTZOID, TIMESTAMPOID
375
447
  column = cast_to_time(column)
448
+ when NUMERIC_COLUMN_TYPE_OID
449
+ column = column.to_d if column.respond_to?(:to_d)
376
450
  end
377
451
 
378
452
  hashed_row[fields[cel_index]] = column
@@ -380,6 +454,7 @@ module ActiveRecord
380
454
  rows << hashed_row
381
455
  end
382
456
  end
457
+ res.clear
383
458
  return rows
384
459
  end
385
460
 
@@ -430,7 +505,7 @@ module ActiveRecord
430
505
  end
431
506
  unescape_bytea(s)
432
507
  end
433
-
508
+
434
509
  # Query a table's column names, default values, and types.
435
510
  #
436
511
  # The underlying query is roughly:
@@ -466,11 +541,13 @@ module ActiveRecord
466
541
  def translate_field_type(field_type)
467
542
  # Match the beginning of field_type since it may have a size constraint on the end.
468
543
  case field_type
544
+ # PostgreSQL array data types.
545
+ when /\[\]$/i then 'string'
469
546
  when /^timestamp/i then 'datetime'
470
547
  when /^real|^money/i then 'float'
471
548
  when /^interval/i then 'string'
472
549
  # geometric types (the line type is currently not implemented in postgresql)
473
- when /^(?:point|lseg|box|"?path"?|polygon|circle)/i then 'string'
550
+ when /^(?:point|lseg|box|"?path"?|polygon|circle)/i then 'string'
474
551
  when /^bytea/i then 'binary'
475
552
  else field_type # Pass through standard types.
476
553
  end
@@ -480,16 +557,16 @@ module ActiveRecord
480
557
  # Boolean types
481
558
  return "t" if value =~ /true/i
482
559
  return "f" if value =~ /false/i
483
-
560
+
484
561
  # Char/String/Bytea type values
485
562
  return $1 if value =~ /^'(.*)'::(bpchar|text|character varying|bytea)$/
486
-
563
+
487
564
  # Numeric values
488
565
  return value if value =~ /^-?[0-9]+(\.[0-9]*)?/
489
566
 
490
567
  # Fixed dates / times
491
568
  return $1 if value =~ /^'(.+)'::(date|timestamp)/
492
-
569
+
493
570
  # Anything else is blank, some user type, or some function
494
571
  # and we can't know the value of that, so return nil.
495
572
  return nil
@@ -499,7 +576,7 @@ module ActiveRecord
499
576
  def cast_to_time(value)
500
577
  return value unless value.class == DateTime
501
578
  v = value
502
- time_array = [v.year, v.month, v.day, v.hour, v.min, v.sec]
579
+ time_array = [v.year, v.month, v.day, v.hour, v.min, v.sec, v.usec]
503
580
  Time.send(Base.default_timezone, *time_array) rescue nil
504
581
  end
505
582
  end
@@ -19,6 +19,9 @@ module ActiveRecord
19
19
  :results_as_hash => true,
20
20
  :type_translation => false
21
21
  )
22
+
23
+ db.busy_timeout(config[:timeout]) unless config[:timeout].nil?
24
+
22
25
  ConnectionAdapters::SQLiteAdapter.new(db, logger)
23
26
  end
24
27
 
@@ -98,6 +101,10 @@ module ActiveRecord
98
101
  def supports_migrations? #:nodoc:
99
102
  true
100
103
  end
104
+
105
+ def requires_reloading?
106
+ true
107
+ end
101
108
 
102
109
  def supports_count_distinct? #:nodoc:
103
110
  false
@@ -110,6 +117,7 @@ module ActiveRecord
110
117
  :text => { :name => "text" },
111
118
  :integer => { :name => "integer" },
112
119
  :float => { :name => "float" },
120
+ :decimal => { :name => "decimal" },
113
121
  :datetime => { :name => "datetime" },
114
122
  :timestamp => { :name => "datetime" },
115
123
  :time => { :name => "datetime" },
@@ -184,6 +192,12 @@ module ActiveRecord
184
192
  end
185
193
 
186
194
 
195
+ # SELECT ... FOR UPDATE is redundant since the table is locked.
196
+ def add_lock!(sql, options) #:nodoc:
197
+ sql
198
+ end
199
+
200
+
187
201
  # SCHEMA STATEMENTS ========================================
188
202
 
189
203
  def tables(name = nil) #:nodoc:
@@ -217,13 +231,13 @@ module ActiveRecord
217
231
  end
218
232
 
219
233
  def rename_table(name, new_name)
220
- move_table(name, new_name)
234
+ execute "ALTER TABLE #{name} RENAME TO #{new_name}"
221
235
  end
222
236
 
223
237
  def add_column(table_name, column_name, type, options = {}) #:nodoc:
224
- alter_table(table_name) do |definition|
225
- definition.column(column_name, type, options)
226
- end
238
+ super(table_name, column_name, type, options)
239
+ # See last paragraph on http://www.sqlite.org/lang_altertable.html
240
+ execute "VACUUM"
227
241
  end
228
242
 
229
243
  def remove_column(table_name, column_name) #:nodoc:
@@ -240,10 +254,11 @@ module ActiveRecord
240
254
 
241
255
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
242
256
  alter_table(table_name) do |definition|
257
+ include_default = options_include_default?(options)
243
258
  definition[column_name].instance_eval do
244
259
  self.type = type
245
- self.limit = options[:limit] if options[:limit]
246
- self.default = options[:default] if options[:default]
260
+ self.limit = options[:limit] if options.include?(:limit)
261
+ self.default = options[:default] if include_default
247
262
  end
248
263
  end
249
264
  end
@@ -306,8 +321,9 @@ module ActiveRecord
306
321
  elsif from == "altered_#{to}"
307
322
  name = name[5..-1]
308
323
  end
309
-
310
- opts = { :name => name }
324
+
325
+ # index name can't be the same
326
+ opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
311
327
  opts[:unique] = true if index.unique
312
328
  add_index(to, index.columns, opts)
313
329
  end
@@ -316,9 +332,10 @@ module ActiveRecord
316
332
  def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
317
333
  column_mappings = Hash[*columns.map {|name| [name, name]}.flatten]
318
334
  rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
319
-
335
+ from_columns = columns(from).collect {|col| col.name}
336
+ columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
320
337
  @connection.execute "SELECT * FROM #{from}" do |row|
321
- sql = "INSERT INTO #{to} VALUES ("
338
+ sql = "INSERT INTO #{to} ("+columns*','+") VALUES ("
322
339
  sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
323
340
  sql << ')'
324
341
  @connection.execute sql
@@ -359,6 +376,17 @@ module ActiveRecord
359
376
  sql
360
377
  end
361
378
  end
379
+
380
+ def rename_table(name, new_name)
381
+ move_table(name, new_name)
382
+ end
383
+
384
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
385
+ alter_table(table_name) do |definition|
386
+ definition.column(column_name, type, options)
387
+ end
388
+ end
389
+
362
390
  end
363
391
 
364
392
  class DeprecatedSQLiteAdapter < SQLite2Adapter # :nodoc: