Skip to content

Commit

Permalink
Allow precision option for postgresql datetimes
Browse files Browse the repository at this point in the history
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:
- [rails#3519](rails#3519)
- [rails#3520](rails#3520)

Also, kudos to @mattscilipoti
  • Loading branch information
tonywok committed Jun 22, 2012
1 parent a232831 commit 6657ec9
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 0 deletions.
12 changes: 12 additions & 0 deletions activerecord/CHANGELOG.md
Original file line number Original file line Diff line number Diff line change
@@ -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:


Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def extract_limit(sql_type)
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
Expand All @@ -201,6 +202,8 @@ def extract_scale(sql_type)
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
Expand Down Expand Up @@ -1285,6 +1288,13 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
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
Expand Down
65 changes: 65 additions & 0 deletions activerecord/test/cases/adapters/postgresql/timestamp_test.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -27,4 +27,69 @@ def test_save_infinity_and_beyond
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

0 comments on commit 6657ec9

Please sign in to comment.