mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Allow precision option for postgresql datetimes
This patch addresses the difficulty of retrieving datetime fields. By default, the database holds a higher precision than the time as a String. This issue is discussed at length at the following links: - [#3519](https://github.com/rails/rails/issues/3519) - [#3520](https://github.com/rails/rails/issues/3520) Also, kudos to @mattscilipoti
This commit is contained in:
parent
a232831f2d
commit
6657ec9a0a
3 changed files with 87 additions and 0 deletions
|
@ -1,5 +1,17 @@
|
||||||
## Rails 4.0.0 (unreleased) ##
|
## Rails 4.0.0 (unreleased) ##
|
||||||
|
|
||||||
|
* Added support for specifying the precision of a timestamp in the postgresql
|
||||||
|
adapter. So, instead of having to incorrectly specify the precision using the
|
||||||
|
`:limit` option, you may use `:precision`, as intended. For example, in a migration:
|
||||||
|
|
||||||
|
def change
|
||||||
|
create_table :foobars do |t|
|
||||||
|
t.timestamps :precision => 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
*Tony Schneider*
|
||||||
|
|
||||||
* Allow ActiveRecord::Relation#pluck to accept multiple columns. Returns an
|
* Allow ActiveRecord::Relation#pluck to accept multiple columns. Returns an
|
||||||
array of arrays containing the typecasted values:
|
array of arrays containing the typecasted values:
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,7 @@ module ActiveRecord
|
||||||
case sql_type
|
case sql_type
|
||||||
when /^bigint/i; 8
|
when /^bigint/i; 8
|
||||||
when /^smallint/i; 2
|
when /^smallint/i; 2
|
||||||
|
when /^timestamp/i; nil
|
||||||
else super
|
else super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -201,6 +202,8 @@ module ActiveRecord
|
||||||
def extract_precision(sql_type)
|
def extract_precision(sql_type)
|
||||||
if sql_type == 'money'
|
if sql_type == 'money'
|
||||||
self.class.money_precision
|
self.class.money_precision
|
||||||
|
elsif sql_type =~ /timestamp/i
|
||||||
|
$1.to_i if sql_type =~ /\((\d+)\)/
|
||||||
else
|
else
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
@ -1285,6 +1288,13 @@ module ActiveRecord
|
||||||
when 5..8; 'bigint'
|
when 5..8; 'bigint'
|
||||||
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
|
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
|
||||||
end
|
end
|
||||||
|
when 'datetime'
|
||||||
|
return super unless precision
|
||||||
|
|
||||||
|
case precision
|
||||||
|
when 0..6; "timestamp(#{precision})"
|
||||||
|
else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,4 +27,69 @@ class TimestampTest < ActiveRecord::TestCase
|
||||||
d = Developer.create!(:name => 'aaron', :updated_at => -1.0 / 0.0)
|
d = Developer.create!(:name => 'aaron', :updated_at => -1.0 / 0.0)
|
||||||
assert_equal(-1.0 / 0.0, d.updated_at)
|
assert_equal(-1.0 / 0.0, d.updated_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_default_datetime_precision
|
||||||
|
ActiveRecord::Base.connection.create_table(:foos)
|
||||||
|
ActiveRecord::Base.connection.add_column :foos, :created_at, :datetime
|
||||||
|
ActiveRecord::Base.connection.add_column :foos, :updated_at, :datetime
|
||||||
|
assert_nil activerecord_column_option('foos', 'created_at', 'precision')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_timestamp_data_type_with_precision
|
||||||
|
ActiveRecord::Base.connection.create_table(:foos)
|
||||||
|
ActiveRecord::Base.connection.add_column :foos, :created_at, :datetime, :precision => 0
|
||||||
|
ActiveRecord::Base.connection.add_column :foos, :updated_at, :datetime, :precision => 5
|
||||||
|
assert_equal 0, activerecord_column_option('foos', 'created_at', 'precision')
|
||||||
|
assert_equal 5, activerecord_column_option('foos', 'updated_at', 'precision')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_timestamps_helper_with_custom_precision
|
||||||
|
ActiveRecord::Base.connection.create_table(:foos) do |t|
|
||||||
|
t.timestamps :precision => 4
|
||||||
|
end
|
||||||
|
assert_equal 4, activerecord_column_option('foos', 'created_at', 'precision')
|
||||||
|
assert_equal 4, activerecord_column_option('foos', 'updated_at', 'precision')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_passing_precision_to_timestamp_does_not_set_limit
|
||||||
|
ActiveRecord::Base.connection.create_table(:foos) do |t|
|
||||||
|
t.timestamps :precision => 4
|
||||||
|
end
|
||||||
|
assert_nil activerecord_column_option("foos", "created_at", "limit")
|
||||||
|
assert_nil activerecord_column_option("foos", "updated_at", "limit")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_invalid_timestamp_precision_raises_error
|
||||||
|
assert_raises ActiveRecord::ActiveRecordError do
|
||||||
|
ActiveRecord::Base.connection.create_table(:foos) do |t|
|
||||||
|
t.timestamps :precision => 7
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_postgres_agrees_with_activerecord_about_precision
|
||||||
|
ActiveRecord::Base.connection.create_table(:foos) do |t|
|
||||||
|
t.timestamps :precision => 4
|
||||||
|
end
|
||||||
|
assert_equal '4', pg_datetime_precision('foos', 'created_at')
|
||||||
|
assert_equal '4', pg_datetime_precision('foos', 'updated_at')
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def pg_datetime_precision(table_name, column_name)
|
||||||
|
results = ActiveRecord::Base.connection.execute("SELECT column_name, datetime_precision FROM information_schema.columns WHERE table_name ='#{table_name}'")
|
||||||
|
result = results.find do |result_hash|
|
||||||
|
result_hash["column_name"] == column_name
|
||||||
|
end
|
||||||
|
result && result["datetime_precision"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def activerecord_column_option(tablename, column_name, option)
|
||||||
|
result = ActiveRecord::Base.connection.columns(tablename).find do |column|
|
||||||
|
column.name == column_name
|
||||||
|
end
|
||||||
|
result && result.send(option)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue