【问题标题】:ActiveRecord/sqlite3 column type lost in table view?ActiveRecord/sqlite3 列类型在表视图中丢失?
【发布时间】:2010-05-06 16:50:49
【问题描述】:

我有以下模仿我的问题的 ActiveRecord 测试用例。我有一个 People 表,其中一个属性是日期。我在该表上创建了一个视图,添加了一列,即该日期加上 20 分钟:

#!/usr/bin/env ruby

%w|pp rubygems active_record irb active_support date|.each {|lib| require lib}

ActiveRecord::Base.establish_connection(
:adapter => "sqlite3",
:database => "test.db"
)

ActiveRecord::Schema.define do
  create_table :people, :force => true do |t|
    t.column :name, :string
    t.column :born_at, :datetime
  end

  execute "create view clowns as select p.name, p.born_at, datetime(p.born_at, '+' || '20' || ' minutes') as twenty_after_born_at from people p;"

end

class Person < ActiveRecord::Base
  validates_presence_of :name
end

class Clown < ActiveRecord::Base
end

Person.create(:name => "John", :born_at => DateTime.now)

pp Person.all.first.born_at.class
pp Clown.all.first.born_at.class
pp Clown.all.first.twenty_after_born_at.class

问题是,输出是

Time
Time
String

当我期望视图的新 datetime 属性也是 ruby​​ 世界中的 Time 或 DateTime 时。有什么想法吗?

我也试过了:

create view clowns as select p.name, p.born_at, CAST(datetime(p.born_at, '+' || '20' || ' minutes') as datetime) as twenty_after_born_at from people p;

结果相同。

【问题讨论】:

    标签: ruby activerecord sqlite types


    【解决方案1】:

    好吧,经过更多调查,我发现:

    MySQL 工作:

    %w|pp rubygems active_record irb active_support date|.each {|lib| require lib}
    
    ActiveRecord::Base.establish_connection(
        :adapter => "mysql",
        :username => "root",
        :database => "test2"
    )
    
    ActiveRecord::Schema.define do
      create_table :people, :force => true do |t|
        t.column :name, :string
        t.column :born_at, :datetime
      end
    
      execute "create view clowns as select p.name, p.born_at, (p.born_at + INTERVAL 20 MINUTE) as twenty_after_born_at from people p;"
    end
    
    class Person < ActiveRecord::Base
      validates_presence_of :name
    end
    
    class Clown < ActiveRecord::Base
    end
    
    Person.create(:name => "John", :born_at => DateTime.now)
    
    pp Person.all.first.born_at.class
    pp Clown.all.first.born_at.class
    pp Clown.all.first.twenty_after_born_at.class
    

    生产:

    Time
    Time
    Time
    

    阅读 sqlite3 适配器源代码,我发现它使用 PRAGMA table_info(table_name) 来获取类型信息,并且不返回视图的类型:

    sqlite> pragma table_info('people');
    0|id|INTEGER|1||1
    1|name|varchar(255)|0||0
    2|born_at|datetime|0||0
    sqlite> pragma table_info('clowns');
    0|name|varchar(255)|0||0
    1|born_at|datetime|0||0
    2|twenty_after_born_at||0||0
    

    因此它可能是适配器的限制或只是 sqlite3 的视图限制。我有opened a ticket for ActiveRecord:

    另外,quoting this mail 在 sqlite 用户中:

    RoR 应该使用 sqlite3_column_type() API 来判断 从 a 返回的值的类型 询问。其他 API,例如 sqlite3_column_decltype() 和编译指示 table_info 正在返回其他 信息,而不是类型 结果值。

    【讨论】:

      【解决方案2】:

      好吧,与 MySQL 相比,SQLite 中基本上没有数据时间类型。在您的示例中,您明确定义了表的类型,但没有指定视图的类型。这可能是问题所在。没碰过红宝石,无法查。

      【讨论】:

      • 问题出在 RoR 和 sqlite3 上。当它知道 sqlite3 无法为您提供视图类型时,第一个使用 table_info PRAGMA 视图。它应该在结果集上使用 sqlite3_column_type (但似乎 ActiveRecord 中的类型只为一个表解析一次)。 Sqlite 的错误是没有在视图中提供类型,当它们可以映射到原始表时,在这种情况下,映射到 datetime() 函数的结果(至少 MySQL 可以)。
      猜你喜欢
      • 1970-01-01
      • 2015-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-12
      • 2011-01-23
      • 1970-01-01
      • 2014-03-30
      相关资源
      最近更新 更多