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.
- data/CHANGELOG +400 -1
- data/README +2 -2
- data/RUNNING_UNIT_TESTS +21 -3
- data/Rakefile +55 -10
- data/lib/active_record.rb +10 -4
- data/lib/active_record/acts/list.rb +15 -4
- data/lib/active_record/acts/nested_set.rb +11 -12
- data/lib/active_record/acts/tree.rb +13 -14
- data/lib/active_record/aggregations.rb +46 -22
- data/lib/active_record/associations.rb +213 -162
- data/lib/active_record/associations/association_collection.rb +45 -15
- data/lib/active_record/associations/association_proxy.rb +32 -13
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +18 -18
- data/lib/active_record/associations/has_many_association.rb +37 -17
- data/lib/active_record/associations/has_many_through_association.rb +120 -30
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +282 -203
- data/lib/active_record/calculations.rb +95 -54
- data/lib/active_record/callbacks.rb +13 -24
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +12 -1
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb.rej +21 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -9
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +121 -37
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -23
- data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -11
- data/lib/active_record/connection_adapters/firebird_adapter.rb +364 -50
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -33
- data/lib/active_record/connection_adapters/openbase_adapter.rb +4 -3
- data/lib/active_record/connection_adapters/oracle_adapter.rb +151 -127
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +125 -48
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +38 -10
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +183 -155
- data/lib/active_record/connection_adapters/sybase_adapter.rb +190 -212
- data/lib/active_record/deprecated_associations.rb +24 -10
- data/lib/active_record/deprecated_finders.rb +4 -1
- data/lib/active_record/fixtures.rb +37 -23
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +8 -5
- data/lib/active_record/observer.rb +73 -34
- data/lib/active_record/reflection.rb +21 -7
- data/lib/active_record/schema_dumper.rb +33 -5
- data/lib/active_record/timestamp.rb +23 -34
- data/lib/active_record/transactions.rb +37 -30
- data/lib/active_record/validations.rb +46 -30
- data/lib/active_record/vendor/mysql.rb +20 -5
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record/wrappings.rb +1 -2
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +5 -1
- data/test/abstract_unit.rb +18 -8
- data/test/{active_schema_mysql.rb → active_schema_test_mysql.rb} +2 -2
- data/test/adapter_test.rb +9 -7
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +29 -0
- data/test/{association_callbacks_test.rb → associations/callbacks_test.rb} +10 -8
- data/test/{associations_cascaded_eager_loading_test.rb → associations/cascaded_eager_loading_test.rb} +35 -3
- data/test/{associations_go_eager_test.rb → associations/eager_test.rb} +36 -2
- data/test/{associations_extensions_test.rb → associations/extension_test.rb} +5 -0
- data/test/{associations_join_model_test.rb → associations/join_model_test.rb} +118 -8
- data/test/associations_test.rb +339 -45
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +321 -67
- data/test/calculations_test.rb +48 -10
- data/test/callbacks_test.rb +13 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +18 -17
- data/test/connections/native_firebird/connection.rb +19 -17
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +18 -15
- data/test/connections/native_openbase/connection.rb +14 -15
- data/test/connections/native_oracle/connection.rb +16 -12
- data/test/connections/native_postgresql/connection.rb +16 -17
- data/test/connections/native_sqlite/connection.rb +3 -6
- data/test/connections/native_sqlite3/connection.rb +3 -6
- data/test/connections/native_sqlserver/connection.rb +16 -17
- data/test/connections/native_sqlserver_odbc/connection.rb +18 -19
- data/test/connections/native_sybase/connection.rb +16 -17
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/defaults_test.rb +52 -10
- data/test/deprecated_associations_test.rb +151 -107
- data/test/deprecated_finder_test.rb +83 -66
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +118 -11
- data/test/fixtures/accounts.yml +6 -1
- data/test/fixtures/author.rb +27 -4
- data/test/fixtures/categorizations.yml +8 -2
- data/test/fixtures/category.rb +1 -2
- data/test/fixtures/comments.yml +0 -6
- data/test/fixtures/companies.yml +6 -1
- data/test/fixtures/company.rb +23 -1
- data/test/fixtures/company_in_module.rb +8 -10
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/customers.yml +9 -0
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +9 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -0
- data/test/fixtures/db_definitions/firebird.sql +13 -1
- data/test/fixtures/db_definitions/frontbase.drop.sql +31 -0
- data/test/fixtures/db_definitions/frontbase.sql +262 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
- data/test/fixtures/db_definitions/mysql.sql +23 -14
- data/test/fixtures/db_definitions/openbase.sql +13 -1
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +29 -2
- data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
- data/test/fixtures/db_definitions/postgresql.sql +13 -3
- data/test/fixtures/db_definitions/schema.rb +29 -1
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +12 -3
- data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
- data/test/fixtures/db_definitions/sqlserver.sql +35 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
- data/test/fixtures/db_definitions/sybase.sql +13 -4
- data/test/fixtures/developer.rb +12 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/funny_jokes.yml +3 -7
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/mixin.rb +15 -0
- data/test/fixtures/mixins.yml +38 -0
- data/test/fixtures/post.rb +3 -2
- data/test/fixtures/project.rb +3 -1
- data/test/fixtures/topic.rb +6 -1
- data/test/fixtures/topics.yml +4 -4
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +45 -0
- data/test/inheritance_test.rb +67 -6
- data/test/lifecycle_test.rb +40 -19
- data/test/locking_test.rb +170 -26
- data/test/method_scoping_test.rb +2 -2
- data/test/migration_test.rb +387 -110
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +14 -2
- data/test/mixin_test.rb +56 -18
- data/test/modules_test.rb +8 -2
- data/test/multiple_db_test.rb +2 -2
- data/test/pk_test.rb +1 -0
- data/test/reflection_test.rb +8 -2
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +40 -4
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +19 -16
- data/test/transactions_test.rb +86 -72
- data/test/validations_test.rb +126 -56
- data/test/xml_serialization_test.rb +125 -0
- metadata +45 -11
- 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]
|
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
|
-
|
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
|
-
|
133
|
-
|
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)
|
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)
|
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
|
-
|
219
|
-
|
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 =
|
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 =
|
288
|
-
SELECT attr.attname, name.nspname, split_part(def.adsrc, '
|
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,
|
300
|
-
|
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
|
-
|
311
|
-
|
312
|
-
|
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
|
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
|
-
|
328
|
-
|
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
|
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
|
-
|
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
|
-
|
225
|
-
|
226
|
-
|
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
|
246
|
-
self.default = options[:default] if
|
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
|
-
|
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:
|